fix: single-source config.json.example, stop backfilling config.json on disk

hate_crack/config.json.example and the root copy had drifted (10 keys
missing from the packaged copy, 4 dead PassGPT keys still shipped,
hcatPath disagreeing). Replace the packaged copy with a symlink to the
root file so there is exactly one set of shipped defaults, and add a
test guard asserting the two stay identical.

Also stop writing missing keys back into the user's config.json on
load: merge defaults into the in-memory config_parser dict only. A
stale example file previously meant wrong defaults got persisted to
disk permanently; now the effective values are always whatever config.json.example
currently ships, and users are never surprised by keys silently
appearing in their config file.

Fixes #150
This commit is contained in:
Justin Bollinger
2026-07-28 17:25:37 -04:00
parent 7cb8cb5d88
commit 01c6099db9
3 changed files with 23 additions and 49 deletions
-42
View File
@@ -1,42 +0,0 @@
{
"hcatPath": "",
"hcatBin": "hashcat",
"hcatTuning": "",
"hcatPotfilePath": "~/.hashcat/hashcat.potfile",
"hcatDebugLogPath": "./hashcat_debug",
"hcatWordlists": "./wordlists",
"rules_directory": "./hashcat/rules",
"hcatDictionaryWordlist": ["rockyou.txt"],
"hcatCombinationWordlist": ["rockyou.txt","rockyou.txt"],
"hcatHybridlist": ["rockyou.txt"],
"hcatMiddleCombinatorMasks": ["2","4"," ","-","_","+",",",".","&"],
"hcatMiddleBaseList": "rockyou.txt",
"hcatThoroughCombinatorMasks": ["0","1","2","3","4","5","6","7","8","9"," ","-","_","+",",","!","#","$","\"","%","&","'","(",")","*",".","/",":",";","<","=",">","?","@","[","\\","]","^","`","{","|","}","~"],
"hcatThoroughBaseList": "rockyou.txt",
"hcatGoodMeasureBaseList": "rockyou.txt",
"hcatPrinceBaseList": "rockyou.txt",
"pipalPath": "/path/to/pipal",
"pipal_count" : 10,
"bandrelmaxruntime": 300,
"bandrel_common_basedwords": "welcome,password,p@ssword,p@$$word,changeme,letmein,summer,winter,spring,springtime,fall,autumn,monday,tuesday,wednesday,thursday,friday,saturday,sunday,january,february,march,april,may,june,july,august,september,october,november,december,christmas,easter,covid19",
"hashview_url": "http://localhost:8443",
"hashview_api_key": "",
"hashmob_api_key": "",
"ollamaModel": "qwen2.5:32b",
"ollamaNumCtx": 2048,
"ollamaTimeout": 300,
"ollamaMaxSampleLines": 500,
"ollamaAutoResearch": true,
"passgptModel": "javirandor/passgpt-10characters",
"passgptMaxCandidates": 1000000,
"passgptBatchSize": 1024,
"passgptTrainingList": "",
"notify_enabled": false,
"notify_pushover_token": "",
"notify_pushover_user": "",
"notify_per_crack_enabled": false,
"notify_attack_allowlist": [],
"notify_suppress_in_orchestrators": true,
"notify_max_cracks_per_burst": 5,
"notify_poll_interval_seconds": 5.0
}
+1
View File
@@ -0,0 +1 @@
../config.json.example
-7
View File
@@ -262,16 +262,9 @@ except json.JSONDecodeError:
print(" This is a package installation issue. Try reinstalling hate_crack.")
sys.exit(1)
_missing_keys = []
for _key, _value in default_config.items():
if _key not in config_parser:
config_parser[_key] = _value
_missing_keys.append(_key)
if _missing_keys:
with open(_config_path, "w") as _cf:
json.dump(config_parser, _cf, indent=2)
print(f"[config] Added {len(_missing_keys)} missing key(s) to {_config_path}")
print(f" Keys: {', '.join(_missing_keys)}")
# Environment variables override config.json so the CLI can be pointed at a
# different Hashview instance (e.g. a local docker stack for the live test
+22
View File
@@ -0,0 +1,22 @@
import json
import os
REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
ROOT_EXAMPLE = os.path.join(REPO_ROOT, "config.json.example")
PACKAGED_EXAMPLE = os.path.join(REPO_ROOT, "hate_crack", "config.json.example")
def test_packaged_example_is_symlink_to_root():
assert os.path.islink(PACKAGED_EXAMPLE), (
"hate_crack/config.json.example must be a symlink to the root "
"config.json.example so there is a single source of truth for defaults"
)
def test_packaged_and_root_examples_have_identical_content():
with open(ROOT_EXAMPLE) as f:
root_config = json.load(f)
with open(PACKAGED_EXAMPLE) as f:
packaged_config = json.load(f)
assert packaged_config == root_config