style: ruff format pass for Notifications submenu

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Justin Bollinger
2026-04-22 19:32:44 -04:00
parent ff4067c1af
commit 9b684bb44c
5 changed files with 71 additions and 38 deletions

View File

@@ -401,7 +401,9 @@ if hcatOptimizedWordlists:
if not os.path.isdir(hcatOptimizedWordlists): if not os.path.isdir(hcatOptimizedWordlists):
fallback_optimized = os.path.join(hate_path, "optimized_wordlists") fallback_optimized = os.path.join(hate_path, "optimized_wordlists")
if os.path.isdir(fallback_optimized): 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}") print(f"[!] Falling back to {fallback_optimized}")
hcatOptimizedWordlists = fallback_optimized hcatOptimizedWordlists = fallback_optimized
else: else:
@@ -417,8 +419,12 @@ pipalPath = config_parser["pipalPath"]
hcatDictionaryWordlist = config_parser["hcatDictionaryWordlist"] hcatDictionaryWordlist = config_parser["hcatDictionaryWordlist"]
hcatHybridlist = config_parser["hcatHybridlist"] hcatHybridlist = config_parser["hcatHybridlist"]
hcatCombinationWordlist = config_parser["hcatCombinationWordlist"] hcatCombinationWordlist = config_parser["hcatCombinationWordlist"]
hcatCombinator3Wordlist = config_parser.get("hcatCombinator3Wordlist", ["rockyou.txt", "rockyou.txt", "rockyou.txt"]) hcatCombinator3Wordlist = config_parser.get(
hcatCombinatorXWordlist = config_parser.get("hcatCombinatorXWordlist", ["rockyou.txt", "rockyou.txt"]) "hcatCombinator3Wordlist", ["rockyou.txt", "rockyou.txt", "rockyou.txt"]
)
hcatCombinatorXWordlist = config_parser.get(
"hcatCombinatorXWordlist", ["rockyou.txt", "rockyou.txt"]
)
hcatMiddleCombinatorMasks = config_parser["hcatMiddleCombinatorMasks"] hcatMiddleCombinatorMasks = config_parser["hcatMiddleCombinatorMasks"]
hcatMiddleBaseList = config_parser["hcatMiddleBaseList"] hcatMiddleBaseList = config_parser["hcatMiddleBaseList"]
hcatThoroughCombinatorMasks = config_parser["hcatThoroughCombinatorMasks"] hcatThoroughCombinatorMasks = config_parser["hcatThoroughCombinatorMasks"]
@@ -584,8 +590,12 @@ hcatDictionaryWordlist = _normalize_wordlist_setting(
hcatCombinationWordlist = _normalize_wordlist_setting( hcatCombinationWordlist = _normalize_wordlist_setting(
hcatCombinationWordlist, wordlists_dir hcatCombinationWordlist, wordlists_dir
) )
hcatCombinator3Wordlist = _normalize_wordlist_setting(hcatCombinator3Wordlist, wordlists_dir) hcatCombinator3Wordlist = _normalize_wordlist_setting(
hcatCombinatorXWordlist = _normalize_wordlist_setting(hcatCombinatorXWordlist, wordlists_dir) hcatCombinator3Wordlist, wordlists_dir
)
hcatCombinatorXWordlist = _normalize_wordlist_setting(
hcatCombinatorXWordlist, wordlists_dir
)
hcatHybridlist = _normalize_wordlist_setting(hcatHybridlist, wordlists_dir) hcatHybridlist = _normalize_wordlist_setting(hcatHybridlist, wordlists_dir)
hcatMiddleBaseList = _normalize_wordlist_setting(hcatMiddleBaseList, wordlists_dir) hcatMiddleBaseList = _normalize_wordlist_setting(hcatMiddleBaseList, wordlists_dir)
hcatThoroughBaseList = _normalize_wordlist_setting(hcatThoroughBaseList, wordlists_dir) hcatThoroughBaseList = _normalize_wordlist_setting(hcatThoroughBaseList, wordlists_dir)
@@ -2397,8 +2407,14 @@ def hcatMarkovTrain(source_file, hcatHashFile):
hcatProcess.wait(timeout=300) hcatProcess.wait(timeout=300)
if hcatProcess.returncode != 0: if hcatProcess.returncode != 0:
_, stderr_data = hcatProcess.communicate() _, stderr_data = hcatProcess.communicate()
err_msg = stderr_data.decode("utf-8", errors="replace") if stderr_data else "Unknown error" err_msg = (
print(f"[!] hcstat2gen.bin failed with code {hcatProcess.returncode}: {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 return False
except subprocess.TimeoutExpired: except subprocess.TimeoutExpired:
print("[!] hcstat2gen.bin timed out after 300 seconds") print("[!] hcstat2gen.bin timed out after 300 seconds")
@@ -3011,7 +3027,9 @@ def cleanup():
if os.path.isfile(out_path): if os.path.isfile(out_path):
print(f"\nCracked passwords combined with original hashes in {out_path}") print(f"\nCracked passwords combined with original hashes in {out_path}")
else: 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...") print("\nCleaning up temporary files...")
if os.path.exists(hcatHashFile + ".masks"): if os.path.exists(hcatHashFile + ".masks"):
os.remove(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 return result.returncode == 0
def wordlist_cutb( def wordlist_cutb(infile: str, outfile: str, offset: int, length: int | None) -> bool:
infile: str, outfile: str, offset: int, length: int | None
) -> bool:
"""Extract a substring from each word starting at offset, optionally limited to length bytes.""" """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") cutb_bin = os.path.join(hate_path, "hashcat-utils/bin/cutb.bin")
cmd = [cutb_bin, str(offset)] 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.""" """Shard wordlist: keep every mod-th line starting at offset."""
gate_bin = os.path.join(hate_path, "hashcat-utils/bin/gate.bin") gate_bin = os.path.join(hate_path, "hashcat-utils/bin/gate.bin")
with open(infile, "rb") as fin, open(outfile, "wb") as fout: 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 return result.returncode == 0
@@ -3871,7 +3889,9 @@ def rules_cleanup(infile: str, outfile: str) -> bool:
def rules_optimize(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 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: with open(infile, "rb") as fin, open(outfile, "wb") as fout:
result = subprocess.run([optimize_path], stdin=fin, stdout=fout) result = subprocess.run([optimize_path], stdin=fin, stdout=fout)
return result.returncode == 0 return result.returncode == 0

View File

@@ -84,8 +84,12 @@ def load_settings(config_parser: dict | None) -> NotifySettings:
defaults = NotifySettings() defaults = NotifySettings()
return NotifySettings( return NotifySettings(
enabled=_coerce_bool(cfg.get("notify_enabled"), defaults.enabled), enabled=_coerce_bool(cfg.get("notify_enabled"), defaults.enabled),
pushover_token=_coerce_str(cfg.get("notify_pushover_token"), defaults.pushover_token), pushover_token=_coerce_str(
pushover_user=_coerce_str(cfg.get("notify_pushover_user"), defaults.pushover_user), 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( per_crack_enabled=_coerce_bool(
cfg.get("notify_per_crack_enabled"), defaults.per_crack_enabled cfg.get("notify_per_crack_enabled"), defaults.per_crack_enabled
), ),

View File

@@ -6,6 +6,7 @@ and resolves ``toggle_notifications`` / ``toggle_per_crack_notifications`` /
We therefore patch that module directly — patching the ``hate_crack.py`` We therefore patch that module directly — patching the ``hate_crack.py``
proxy would have no effect on the submenu's internal dispatch. proxy would have no effect on the submenu's internal dispatch.
""" """
import hate_crack.main as _main_mod import hate_crack.main as _main_mod
import hate_crack.menu as _menu_mod import hate_crack.menu as _menu_mod
from hate_crack.notify.settings import NotifySettings from hate_crack.notify.settings import NotifySettings

View File

@@ -1,4 +1,5 @@
"""Unit tests for the toggle_per_crack_enabled runtime toggle.""" """Unit tests for the toggle_per_crack_enabled runtime toggle."""
import importlib.util import importlib.util
import json import json
from pathlib import Path from pathlib import Path
@@ -80,9 +81,7 @@ class TestTogglePerCrackNotificationsUI:
from hate_crack.notify.settings import NotifySettings from hate_crack.notify.settings import NotifySettings
settings = NotifySettings(enabled=enabled, per_crack_enabled=per_crack) settings = NotifySettings(enabled=enabled, per_crack_enabled=per_crack)
monkeypatch.setattr( monkeypatch.setattr(CLI_MODULE._notify, "get_settings", lambda: settings)
CLI_MODULE._notify, "get_settings", lambda: settings
)
return settings return settings
def test_guard_refuses_on_when_global_off(self, monkeypatch, capsys): def test_guard_refuses_on_when_global_off(self, monkeypatch, capsys):

View File

@@ -1,4 +1,5 @@
"""Unit tests for hate_crack.notify.settings.""" """Unit tests for hate_crack.notify.settings."""
import json import json
from pathlib import Path from pathlib import Path
@@ -40,16 +41,18 @@ class TestLoadSettings:
assert load_settings(None) == NotifySettings() assert load_settings(None) == NotifySettings()
def test_load_full_dict(self) -> None: def test_load_full_dict(self) -> None:
s = load_settings({ s = load_settings(
"notify_enabled": True, {
"notify_pushover_token": "tok", "notify_enabled": True,
"notify_pushover_user": "usr", "notify_pushover_token": "tok",
"notify_per_crack_enabled": True, "notify_pushover_user": "usr",
"notify_attack_allowlist": ["Brute Force", "Dictionary"], "notify_per_crack_enabled": True,
"notify_suppress_in_orchestrators": False, "notify_attack_allowlist": ["Brute Force", "Dictionary"],
"notify_max_cracks_per_burst": 20, "notify_suppress_in_orchestrators": False,
"notify_poll_interval_seconds": 2.5, "notify_max_cracks_per_burst": 20,
}) "notify_poll_interval_seconds": 2.5,
}
)
assert s.enabled is True assert s.enabled is True
assert s.pushover_token == "tok" assert s.pushover_token == "tok"
assert s.pushover_user == "usr" assert s.pushover_user == "usr"
@@ -60,12 +63,14 @@ class TestLoadSettings:
assert s.poll_interval_seconds == 2.5 assert s.poll_interval_seconds == 2.5
def test_load_tolerates_bad_types(self) -> None: def test_load_tolerates_bad_types(self) -> None:
s = load_settings({ s = load_settings(
"notify_enabled": "true", {
"notify_max_cracks_per_burst": "not-a-number", "notify_enabled": "true",
"notify_poll_interval_seconds": "also-bad", "notify_max_cracks_per_burst": "not-a-number",
"notify_attack_allowlist": "not-a-list", "notify_poll_interval_seconds": "also-bad",
}) "notify_attack_allowlist": "not-a-list",
}
)
# string "true" -> True # string "true" -> True
assert s.enabled is True assert s.enabled is True
# bad ints fall back to defaults (5, 5.0) # 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: def test_preserves_other_entries(self, tmp_path: Path) -> None:
config_path = tmp_path / "config.json" config_path = tmp_path / "config.json"
config_path.write_text(json.dumps({ config_path.write_text(
"hcatBin": "hashcat", json.dumps(
"notify_attack_allowlist": ["Existing"], {
})) "hcatBin": "hashcat",
"notify_attack_allowlist": ["Existing"],
}
)
)
add_to_allowlist(str(config_path), "Brute Force") add_to_allowlist(str(config_path), "Brute Force")
data = json.loads(config_path.read_text()) data = json.loads(config_path.read_text())
assert data["hcatBin"] == "hashcat" assert data["hcatBin"] == "hashcat"