From 513f6df459ac1aba2dcf7196a95c052b58aa5705 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Mon, 14 Jul 2025 20:26:17 -0400 Subject: [PATCH] Fix QR menu option case handling --- src/password_manager/entry_management.py | 4 +-- src/password_manager/manager.py | 14 ++++++++ src/tests/test_nostr_qr.py | 43 ++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/password_manager/entry_management.py b/src/password_manager/entry_management.py index 0aaed6a..f2e871c 100644 --- a/src/password_manager/entry_management.py +++ b/src/password_manager/entry_management.py @@ -437,8 +437,8 @@ class EntryManager: """Return the npub and nsec for the specified entry.""" entry = self.retrieve_entry(index) - etype = entry.get("type") if entry else None - kind = entry.get("kind") if entry else None + etype = entry.get("type", "").lower() if entry else "" + kind = entry.get("kind", "").lower() if entry else "" if not entry or ( etype != EntryType.NOSTR.value and kind != EntryType.NOSTR.value ): diff --git a/src/password_manager/manager.py b/src/password_manager/manager.py index a574161..f0216d3 100644 --- a/src/password_manager/manager.py +++ b/src/password_manager/manager.py @@ -1841,6 +1841,8 @@ class PasswordManager: ) archived = entry.get("archived", entry.get("blacklisted", False)) entry_type = entry.get("type", entry.get("kind", EntryType.PASSWORD.value)) + if isinstance(entry_type, str): + entry_type = entry_type.lower() print(colored("\n[+] Entry Actions:", "green")) if archived: print(colored("U. Unarchive", "cyan")) @@ -1923,6 +1925,8 @@ class PasswordManager: def _entry_edit_menu(self, index: int, entry: dict) -> None: """Sub-menu for editing common entry fields.""" entry_type = entry.get("type", entry.get("kind", EntryType.PASSWORD.value)) + if isinstance(entry_type, str): + entry_type = entry_type.lower() while True: fp, parent_fp, child_fp = self.header_fingerprint_args clear_header_with_notification( @@ -1983,6 +1987,8 @@ class PasswordManager: """Display QR codes for the given ``entry``.""" entry_type = entry.get("type", entry.get("kind")) + if isinstance(entry_type, str): + entry_type = entry_type.lower() try: if entry_type in {EntryType.SEED.value, EntryType.MANAGED_ACCOUNT.value}: @@ -2069,6 +2075,8 @@ class PasswordManager: return entry_type = entry.get("type", entry.get("kind", EntryType.PASSWORD.value)) + if isinstance(entry_type, str): + entry_type = entry_type.lower() if entry_type == EntryType.TOTP.value: label = entry.get("label", "") @@ -2524,6 +2532,8 @@ class PasswordManager: return entry_type = entry.get("type", entry.get("kind", EntryType.PASSWORD.value)) + if isinstance(entry_type, str): + entry_type = entry_type.lower() if entry_type == EntryType.TOTP.value: label = entry.get("label", "") @@ -2917,6 +2927,8 @@ class PasswordManager: return etype = entry.get("type", entry.get("kind", EntryType.PASSWORD.value)) + if isinstance(etype, str): + etype = etype.lower() print(color_text(f"Index: {index}", "index")) if etype == EntryType.TOTP.value: print(color_text(f" Label: {entry.get('label', '')}", "index")) @@ -3815,6 +3827,8 @@ class PasswordManager: counts: dict[str, int] = {etype.value: 0 for etype in EntryType} for entry in entries.values(): etype = entry.get("type", entry.get("kind", EntryType.PASSWORD.value)) + if isinstance(etype, str): + etype = etype.lower() counts[etype] = counts.get(etype, 0) + 1 stats["entries"] = counts stats["total_entries"] = len(entries) diff --git a/src/tests/test_nostr_qr.py b/src/tests/test_nostr_qr.py index e2af078..0ad7fe3 100644 --- a/src/tests/test_nostr_qr.py +++ b/src/tests/test_nostr_qr.py @@ -93,3 +93,46 @@ def test_show_private_key_qr(monkeypatch, capsys): out = capsys.readouterr().out assert called == [nsec] assert color_text(f"nsec: {nsec}", "deterministic") in out + + +def test_qr_menu_case_insensitive(monkeypatch): + """QR menu should appear even if entry type is uppercase.""" + with TemporaryDirectory() as tmpdir: + tmp_path = Path(tmpdir) + vault, enc_mgr = create_vault(tmp_path, TEST_SEED, TEST_PASSWORD) + cfg_mgr = ConfigManager(vault, tmp_path) + backup_mgr = BackupManager(tmp_path, cfg_mgr) + entry_mgr = EntryManager(vault, backup_mgr) + + pm = PasswordManager.__new__(PasswordManager) + pm.encryption_mode = EncryptionMode.SEED_ONLY + pm.encryption_manager = enc_mgr + pm.vault = vault + pm.entry_manager = entry_mgr + pm.backup_manager = backup_mgr + pm.parent_seed = TEST_SEED + pm.nostr_client = FakeNostrClient() + pm.fingerprint_dir = tmp_path + pm.is_dirty = False + pm.secret_mode_enabled = False + + idx = entry_mgr.add_nostr_key("main") + npub, _ = entry_mgr.get_nostr_key_pair(idx, TEST_SEED) + + # Modify index to use uppercase type/kind + data = enc_mgr.load_json_data(entry_mgr.index_file) + data["entries"][str(idx)]["type"] = "NOSTR" + data["entries"][str(idx)]["kind"] = "NOSTR" + enc_mgr.save_json_data(data, entry_mgr.index_file) + entry_mgr._index_cache = None + + inputs = iter([str(idx), "q", "p", ""]) + monkeypatch.setattr("builtins.input", lambda *a, **k: next(inputs)) + called = [] + monkeypatch.setattr( + "password_manager.manager.TotpManager.print_qr_code", + lambda data: called.append(data), + ) + + pm.handle_retrieve_entry() + assert called == [f"nostr:{npub}"]