From 40f37b30bd2bdc1e2bfea3eb4bb1479e3133eee5 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 26 Feb 2026 18:11:07 +0000 Subject: [PATCH] Refactor display_sensitive_entry_info into DisplayService Extracted the monolithic `display_sensitive_entry_info` method from `PasswordManager` into a new `DisplayService` class in `src/seedpass/core/display_service.py`. This improves maintainability and separation of concerns. - Created `src/seedpass/core/display_service.py` with `DisplayService` class. - Implemented specific display methods for each entry type (TOTP, SSH, SEED, PGP, Nostr, Key/Value, Managed Account, Password). - Updated `src/seedpass/core/manager.py` to use `DisplayService`. - Verified with existing tests `test_manager_retrieve_totp.py` and `test_manager_search_display.py`. Co-authored-by: PR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> --- src/seedpass/core/display_service.py | 458 +++++++++++++++++++++++++++ src/seedpass/core/manager.py | 420 +----------------------- 2 files changed, 470 insertions(+), 408 deletions(-) create mode 100644 src/seedpass/core/display_service.py diff --git a/src/seedpass/core/display_service.py b/src/seedpass/core/display_service.py new file mode 100644 index 00000000..d49a733a --- /dev/null +++ b/src/seedpass/core/display_service.py @@ -0,0 +1,458 @@ +from __future__ import annotations + +import logging +import sys +import time +from typing import TYPE_CHECKING + +from termcolor import colored + +from seedpass.core.entry_types import EntryType +from seedpass.core.totp import TotpManager +from utils.clipboard import copy_to_clipboard +from utils.color_scheme import color_text +from utils.input_utils import timed_input +from utils.password_prompt import confirm_action + +if TYPE_CHECKING: + from seedpass.core.manager import PasswordManager + +logger = logging.getLogger(__name__) + + +class DisplayService: + """Service for displaying sensitive entry information.""" + + def __init__(self, manager: PasswordManager) -> None: + self.manager = manager + + def display_sensitive_entry_info(self, entry: dict, index: int) -> None: + """Display information for a sensitive entry. + + Parameters + ---------- + entry: dict + Entry data retrieved from the vault. + index: int + Index of the entry being displayed. + """ + # Access private attribute directly as we are moving logic from manager + self.manager._suppress_entry_actions_menu = False + + entry_type = self.manager._entry_type_str(entry) + + if entry_type == EntryType.TOTP.value: + self._display_totp(entry, index) + elif entry_type == EntryType.SSH.value: + self._display_ssh(entry, index) + elif entry_type == EntryType.SEED.value: + self._display_seed(entry, index) + elif entry_type == EntryType.PGP.value: + self._display_pgp(entry, index) + elif entry_type == EntryType.NOSTR.value: + self._display_nostr(entry, index) + elif entry_type == EntryType.KEY_VALUE.value: + self._display_key_value(entry, index) + elif entry_type == EntryType.MANAGED_ACCOUNT.value: + self._display_managed_account(entry, index) + else: + self._display_password(entry, index) + + def _display_totp(self, entry: dict, index: int) -> None: + label = entry.get("label", "") + period = int(entry.get("period", 30)) + notes = entry.get("notes", "") + print(colored(f"Retrieving 2FA code for '{label}'.", "cyan")) + print(colored("Press Enter to return to the menu.", "cyan")) + try: + key = self.manager.KEY_TOTP_DET or getattr(self.manager, "parent_seed", None) + secret = self.manager.entry_manager.get_totp_secret(index, key) + while True: + code = TotpManager.current_code_from_secret(secret) + if self.manager.secret_mode_enabled: + if copy_to_clipboard(code, self.manager.clipboard_clear_delay): + print( + colored( + f"[+] 2FA code for '{label}' copied to clipboard. Will clear in {self.manager.clipboard_clear_delay} seconds.", + "green", + ) + ) + else: + print(colored("\n[+] Retrieved 2FA Code:\n", "green")) + print(colored(f"Label: {label}", "cyan")) + imported = "secret" in entry + category = "imported" if imported else "deterministic" + print(color_text(f"Code: {code}", category)) + if notes: + print(colored(f"Notes: {notes}", "cyan")) + tags = entry.get("tags", []) + if tags: + print(colored(f"Tags: {', '.join(tags)}", "cyan")) + remaining = self.manager.entry_manager.get_totp_time_remaining(index) + exit_loop = False + while remaining > 0: + filled = int(20 * (period - remaining) / period) + bar = "[" + "#" * filled + "-" * (20 - filled) + "]" + sys.stdout.write(f"\r{bar} {remaining:2d}s") + sys.stdout.flush() + try: + user_input = timed_input("", 1) + if ( + user_input.strip() == "" + or user_input.strip().lower() == "b" + ): + exit_loop = True + break + except TimeoutError: + pass + except KeyboardInterrupt: + exit_loop = True + print() + break + remaining -= 1 + sys.stdout.write("\n") + sys.stdout.flush() + if exit_loop: + break + except Exception as e: + logger.error(f"Error generating TOTP code: {e}", exc_info=True) + print(colored(f"Error: Failed to generate TOTP code: {e}", "red")) + + def _display_ssh(self, entry: dict, index: int) -> None: + notes = entry.get("notes", "") + label = entry.get("label", "") + if not confirm_action( + "WARNING: Displaying SSH keys reveals sensitive information. Continue? (Y/N): " + ): + self.manager.notify("SSH key display cancelled.", level="WARNING") + return + try: + priv_pem, pub_pem = self.manager.entry_manager.get_ssh_key_pair( + index, self.manager.parent_seed + ) + print(colored("\n[+] Retrieved SSH Key Pair:\n", "green")) + if label: + print(colored(f"Label: {label}", "cyan")) + if notes: + print(colored(f"Notes: {notes}", "cyan")) + tags = entry.get("tags", []) + if tags: + print(colored(f"Tags: {', '.join(tags)}", "cyan")) + print(colored("Public Key:", "cyan")) + print(color_text(pub_pem, "default")) + if self.manager.secret_mode_enabled: + if copy_to_clipboard(priv_pem, self.manager.clipboard_clear_delay): + print( + colored( + f"[+] SSH private key copied to clipboard. Will clear in {self.manager.clipboard_clear_delay} seconds.", + "green", + ) + ) + else: + print(colored("Private Key:", "cyan")) + print(color_text(priv_pem, "deterministic")) + except Exception as e: + logger.error(f"Error deriving SSH key pair: {e}", exc_info=True) + print(colored(f"Error: Failed to derive SSH keys: {e}", "red")) + + def _display_seed(self, entry: dict, index: int) -> None: + notes = entry.get("notes", "") + label = entry.get("label", "") + if not confirm_action( + "WARNING: Displaying the seed phrase reveals sensitive information. Continue? (Y/N): " + ): + self.manager.notify("Seed phrase display cancelled.", level="WARNING") + return + try: + phrase = self.manager.entry_manager.get_seed_phrase( + index, self.manager.parent_seed + ) + print(colored("\n[+] Retrieved Seed Phrase:\n", "green")) + print(colored(f"Index: {index}", "cyan")) + if label: + print(colored(f"Label: {label}", "cyan")) + if notes: + print(colored(f"Notes: {notes}", "cyan")) + tags = entry.get("tags", []) + if tags: + print(colored(f"Tags: {', '.join(tags)}", "cyan")) + if self.manager.secret_mode_enabled: + if copy_to_clipboard(phrase, self.manager.clipboard_clear_delay): + print( + colored( + f"[+] Seed phrase copied to clipboard. Will clear in {self.manager.clipboard_clear_delay} seconds.", + "green", + ) + ) + else: + print(color_text(phrase, "deterministic")) + if confirm_action("Show derived entropy as hex? (Y/N): "): + from local_bip85.bip85 import BIP85 + from bip_utils import Bip39SeedGenerator + + words = int(entry.get("word_count", entry.get("words", 24))) + entropy_bytes = {12: 16, 18: 24, 24: 32}.get(words, 32) + seed_bytes = Bip39SeedGenerator(self.manager.parent_seed).Generate() + bip85 = BIP85(seed_bytes) + entropy = bip85.derive_entropy( + index=int(entry.get("index", index)), + entropy_bytes=entropy_bytes, + app_no=39, + word_count=words, + ) + print(color_text(f"Entropy: {entropy.hex()}", "deterministic")) + except Exception as e: + logger.error(f"Error deriving seed phrase: {e}", exc_info=True) + print(colored(f"Error: Failed to derive seed phrase: {e}", "red")) + + def _display_pgp(self, entry: dict, index: int) -> None: + notes = entry.get("notes", "") + label = entry.get("user_id", "") + if not confirm_action( + "WARNING: Displaying the PGP key reveals sensitive information. Continue? (Y/N): " + ): + self.manager.notify("PGP key display cancelled.", level="WARNING") + return + try: + priv_key, fingerprint = self.manager.entry_manager.get_pgp_key( + index, self.manager.parent_seed + ) + print(colored("\n[+] Retrieved PGP Key:\n", "green")) + if label: + print(colored(f"User ID: {label}", "cyan")) + if notes: + print(colored(f"Notes: {notes}", "cyan")) + tags = entry.get("tags", []) + if tags: + print(colored(f"Tags: {', '.join(tags)}", "cyan")) + print(colored(f"Fingerprint: {fingerprint}", "cyan")) + if self.manager.secret_mode_enabled: + if copy_to_clipboard(priv_key, self.manager.clipboard_clear_delay): + print( + colored( + f"[+] PGP key copied to clipboard. Will clear in {self.manager.clipboard_clear_delay} seconds.", + "green", + ) + ) + else: + print(color_text(priv_key, "deterministic")) + except Exception as e: + logger.error(f"Error deriving PGP key: {e}", exc_info=True) + print(colored(f"Error: Failed to derive PGP key: {e}", "red")) + + def _display_nostr(self, entry: dict, index: int) -> None: + label = entry.get("label", "") + notes = entry.get("notes", "") + try: + npub, nsec = self.manager.entry_manager.get_nostr_key_pair( + index, self.manager.parent_seed + ) + print(colored("\n[+] Retrieved Nostr Keys:\n", "green")) + print(colored(f"Label: {label}", "cyan")) + print(colored(f"npub: {npub}", "cyan")) + if self.manager.secret_mode_enabled: + if copy_to_clipboard(nsec, self.manager.clipboard_clear_delay): + print( + colored( + f"[+] nsec copied to clipboard. Will clear in {self.manager.clipboard_clear_delay} seconds.", + "green", + ) + ) + else: + print(color_text(f"nsec: {nsec}", "deterministic")) + if notes: + print(colored(f"Notes: {notes}", "cyan")) + tags = entry.get("tags", []) + if tags: + print(colored(f"Tags: {', '.join(tags)}", "cyan")) + except Exception as e: + logger.error(f"Error deriving Nostr keys: {e}", exc_info=True) + print(colored(f"Error: Failed to derive Nostr keys: {e}", "red")) + + def _display_key_value(self, entry: dict, index: int) -> None: + label = entry.get("label", "") + value = entry.get("value", "") + notes = entry.get("notes", "") + archived = entry.get("archived", False) + print(colored(f"Retrieving value for key '{label}'.", "cyan")) + if notes: + print(colored(f"Notes: {notes}", "cyan")) + tags = entry.get("tags", []) + if tags: + print(colored(f"Tags: {', '.join(tags)}", "cyan")) + print( + colored( + f"Archived Status: {'Archived' if archived else 'Active'}", "cyan" + ) + ) + if self.manager.secret_mode_enabled: + if copy_to_clipboard(value, self.manager.clipboard_clear_delay): + print( + colored( + f"[+] Value copied to clipboard. Will clear in {self.manager.clipboard_clear_delay} seconds.", + "green", + ) + ) + else: + print(color_text(f"Value: {value}", "deterministic")) + + custom_fields = entry.get("custom_fields", []) + if custom_fields: + print(colored("Additional Fields:", "cyan")) + hidden_fields = [] + for field in custom_fields: + f_label = field.get("label", "") + f_value = field.get("value", "") + if field.get("is_hidden"): + hidden_fields.append((f_label, f_value)) + print(colored(f" {f_label}: [hidden]", "cyan")) + else: + print(colored(f" {f_label}: {f_value}", "cyan")) + if hidden_fields: + show = input("Reveal hidden fields? (y/N): ").strip().lower() + if show == "y": + for f_label, f_value in hidden_fields: + if self.manager.secret_mode_enabled: + if copy_to_clipboard( + f_value, self.manager.clipboard_clear_delay + ): + print( + colored( + f"[+] {f_label} copied to clipboard. Will clear in {self.manager.clipboard_clear_delay} seconds.", + "green", + ) + ) + else: + print(colored(f" {f_label}: {f_value}", "cyan")) + + def _display_managed_account(self, entry: dict, index: int) -> None: + label = entry.get("label", "") + notes = entry.get("notes", "") + archived = entry.get("archived", False) + fingerprint = entry.get("fingerprint", "") + print(colored(f"Managed account '{label}'.", "cyan")) + if notes: + print(colored(f"Notes: {notes}", "cyan")) + if fingerprint: + print(colored(f"Fingerprint: {fingerprint}", "cyan")) + tags = entry.get("tags", []) + if tags: + print(colored(f"Tags: {', '.join(tags)}", "cyan")) + print( + colored( + f"Archived Status: {'Archived' if archived else 'Active'}", "cyan" + ) + ) + action = ( + input( + "Enter 'r' to reveal seed, 'l' to load account, or press Enter to go back: " + ) + .strip() + .lower() + ) + if action == "r": + seed = self.manager.entry_manager.get_managed_account_seed( + index, self.manager.parent_seed + ) + if self.manager.secret_mode_enabled: + if copy_to_clipboard(seed, self.manager.clipboard_clear_delay): + print( + colored( + f"[+] Seed phrase copied to clipboard. Will clear in {self.manager.clipboard_clear_delay} seconds.", + "green", + ) + ) + else: + print(color_text(seed, "deterministic")) + return + if action == "l": + self.manager._suppress_entry_actions_menu = True + self.manager.load_managed_account(index) + return + + def _display_password(self, entry: dict, index: int) -> None: + website_name = entry.get("label", entry.get("website")) + length = entry.get("length") + username = entry.get("username") + url = entry.get("url") + blacklisted = entry.get("archived", entry.get("blacklisted")) + notes = entry.get("notes", "") + + print( + colored( + f"Retrieving password for '{website_name}' with length {length}.", + "cyan", + ) + ) + if username: + print(colored(f"Username: {username}", "cyan")) + if url: + print(colored(f"URL: {url}", "cyan")) + if blacklisted: + self.manager.notify( + "Warning: This password is archived and should not be used.", + level="WARNING", + ) + + password = self.manager._generate_password_for_entry(entry, index, length) + + if password: + if self.manager.secret_mode_enabled: + if copy_to_clipboard(password, self.manager.clipboard_clear_delay): + print( + colored( + f"[+] Password for '{website_name}' copied to clipboard. Will clear in {self.manager.clipboard_clear_delay} seconds.", + "green", + ) + ) + else: + print( + colored( + f"\n[+] Retrieved Password for {website_name}:\n", + "green", + ) + ) + print(color_text(f"Password: {password}", "deterministic")) + print(colored(f"Associated Username: {username or 'N/A'}", "cyan")) + print(colored(f"Associated URL: {url or 'N/A'}", "cyan")) + print( + colored( + f"Archived Status: {'Archived' if blacklisted else 'Active'}", + "cyan", + ) + ) + if notes: + print(colored(f"Notes: {notes}", "cyan")) + tags = entry.get("tags", []) + if tags: + print(colored(f"Tags: {', '.join(tags)}", "cyan")) + custom_fields = entry.get("custom_fields", []) + if custom_fields: + print(colored("Additional Fields:", "cyan")) + hidden_fields = [] + for field in custom_fields: + label = field.get("label", "") + value = field.get("value", "") + if field.get("is_hidden"): + hidden_fields.append((label, value)) + print(colored(f" {label}: [hidden]", "cyan")) + else: + print(colored(f" {label}: {value}", "cyan")) + if hidden_fields: + show = input("Reveal hidden fields? (y/N): ").strip().lower() + if show == "y": + for label, value in hidden_fields: + if self.manager.secret_mode_enabled: + if copy_to_clipboard( + value, self.manager.clipboard_clear_delay + ): + print( + colored( + f"[+] {label} copied to clipboard. Will clear in {self.manager.clipboard_clear_delay} seconds.", + "green", + ) + ) + else: + print(colored(f" {label}: {value}", "cyan")) + else: + print(colored("Error: Failed to retrieve the password.", "red")) diff --git a/src/seedpass/core/manager.py b/src/seedpass/core/manager.py index 1531bade..c77f36af 100644 --- a/src/seedpass/core/manager.py +++ b/src/seedpass/core/manager.py @@ -113,6 +113,7 @@ from .stats_manager import StatsManager from .menu_handler import MenuHandler from .profile_service import ProfileService from .entry_service import EntryService +from .display_service import DisplayService # Instantiate the logger logger = logging.getLogger(__name__) @@ -304,11 +305,13 @@ class PasswordManager: self._menu_handler: MenuHandler | None = None self._profile_service: ProfileService | None = None self._entry_service: EntryService | None = None + self._display_service: DisplayService | None = None # Initialize service instances self.menu_handler self.profile_service self.entry_service + self.display_service # Initialize the fingerprint manager first self.initialize_fingerprint_manager() @@ -606,6 +609,14 @@ class PasswordManager: self._entry_service = EntryService(self) return self._entry_service + @property + def display_service(self) -> DisplayService: + if getattr(self, "is_locked", False): + raise VaultLockedError("Vault is locked") + if getattr(self, "_display_service", None) is None: + self._display_service = DisplayService(self) + return self._display_service + def lock_vault(self) -> None: """Clear sensitive information from memory.""" if self.entry_manager is not None: @@ -3025,414 +3036,7 @@ class PasswordManager: Index of the entry being displayed. """ - self._suppress_entry_actions_menu = False - - entry_type = self._entry_type_str(entry) - - if entry_type == EntryType.TOTP.value: - label = entry.get("label", "") - period = int(entry.get("period", 30)) - notes = entry.get("notes", "") - print(colored(f"Retrieving 2FA code for '{label}'.", "cyan")) - print(colored("Press Enter to return to the menu.", "cyan")) - try: - key = self.KEY_TOTP_DET or getattr(self, "parent_seed", None) - secret = self.entry_manager.get_totp_secret(index, key) - while True: - code = TotpManager.current_code_from_secret(secret) - if self.secret_mode_enabled: - if copy_to_clipboard(code, self.clipboard_clear_delay): - print( - colored( - f"[+] 2FA code for '{label}' copied to clipboard. Will clear in {self.clipboard_clear_delay} seconds.", - "green", - ) - ) - else: - print(colored("\n[+] Retrieved 2FA Code:\n", "green")) - print(colored(f"Label: {label}", "cyan")) - imported = "secret" in entry - category = "imported" if imported else "deterministic" - print(color_text(f"Code: {code}", category)) - if notes: - print(colored(f"Notes: {notes}", "cyan")) - tags = entry.get("tags", []) - if tags: - print(colored(f"Tags: {', '.join(tags)}", "cyan")) - remaining = self.entry_manager.get_totp_time_remaining(index) - exit_loop = False - while remaining > 0: - filled = int(20 * (period - remaining) / period) - bar = "[" + "#" * filled + "-" * (20 - filled) + "]" - sys.stdout.write(f"\r{bar} {remaining:2d}s") - sys.stdout.flush() - try: - user_input = timed_input("", 1) - if ( - user_input.strip() == "" - or user_input.strip().lower() == "b" - ): - exit_loop = True - break - except TimeoutError: - pass - except KeyboardInterrupt: - exit_loop = True - print() - break - remaining -= 1 - sys.stdout.write("\n") - sys.stdout.flush() - if exit_loop: - break - except Exception as e: # pragma: no cover - best effort - logging.error(f"Error generating TOTP code: {e}", exc_info=True) - print(colored(f"Error: Failed to generate TOTP code: {e}", "red")) - return - - if entry_type == EntryType.SSH.value: - notes = entry.get("notes", "") - label = entry.get("label", "") - if not confirm_action( - "WARNING: Displaying SSH keys reveals sensitive information. Continue? (Y/N): " - ): - self.notify("SSH key display cancelled.", level="WARNING") - return - try: - priv_pem, pub_pem = self.entry_manager.get_ssh_key_pair( - index, self.parent_seed - ) - print(colored("\n[+] Retrieved SSH Key Pair:\n", "green")) - if label: - print(colored(f"Label: {label}", "cyan")) - if notes: - print(colored(f"Notes: {notes}", "cyan")) - tags = entry.get("tags", []) - if tags: - print(colored(f"Tags: {', '.join(tags)}", "cyan")) - print(colored("Public Key:", "cyan")) - print(color_text(pub_pem, "default")) - if self.secret_mode_enabled: - if copy_to_clipboard(priv_pem, self.clipboard_clear_delay): - print( - colored( - f"[+] SSH private key copied to clipboard. Will clear in {self.clipboard_clear_delay} seconds.", - "green", - ) - ) - else: - print(colored("Private Key:", "cyan")) - print(color_text(priv_pem, "deterministic")) - except Exception as e: # pragma: no cover - best effort - logging.error(f"Error deriving SSH key pair: {e}", exc_info=True) - print(colored(f"Error: Failed to derive SSH keys: {e}", "red")) - return - - if entry_type == EntryType.SEED.value: - notes = entry.get("notes", "") - label = entry.get("label", "") - if not confirm_action( - "WARNING: Displaying the seed phrase reveals sensitive information. Continue? (Y/N): " - ): - self.notify("Seed phrase display cancelled.", level="WARNING") - return - try: - phrase = self.entry_manager.get_seed_phrase(index, self.parent_seed) - print(colored("\n[+] Retrieved Seed Phrase:\n", "green")) - print(colored(f"Index: {index}", "cyan")) - if label: - print(colored(f"Label: {label}", "cyan")) - if notes: - print(colored(f"Notes: {notes}", "cyan")) - tags = entry.get("tags", []) - if tags: - print(colored(f"Tags: {', '.join(tags)}", "cyan")) - if self.secret_mode_enabled: - if copy_to_clipboard(phrase, self.clipboard_clear_delay): - print( - colored( - f"[+] Seed phrase copied to clipboard. Will clear in {self.clipboard_clear_delay} seconds.", - "green", - ) - ) - else: - print(color_text(phrase, "deterministic")) - if confirm_action("Show derived entropy as hex? (Y/N): "): - from local_bip85.bip85 import BIP85 - from bip_utils import Bip39SeedGenerator - - words = int(entry.get("word_count", entry.get("words", 24))) - entropy_bytes = {12: 16, 18: 24, 24: 32}.get(words, 32) - seed_bytes = Bip39SeedGenerator(self.parent_seed).Generate() - bip85 = BIP85(seed_bytes) - entropy = bip85.derive_entropy( - index=int(entry.get("index", index)), - entropy_bytes=entropy_bytes, - app_no=39, - word_count=words, - ) - print(color_text(f"Entropy: {entropy.hex()}", "deterministic")) - except Exception as e: # pragma: no cover - best effort - logging.error(f"Error deriving seed phrase: {e}", exc_info=True) - print(colored(f"Error: Failed to derive seed phrase: {e}", "red")) - return - - if entry_type == EntryType.PGP.value: - notes = entry.get("notes", "") - label = entry.get("user_id", "") - if not confirm_action( - "WARNING: Displaying the PGP key reveals sensitive information. Continue? (Y/N): " - ): - self.notify("PGP key display cancelled.", level="WARNING") - return - try: - priv_key, fingerprint = self.entry_manager.get_pgp_key( - index, self.parent_seed - ) - print(colored("\n[+] Retrieved PGP Key:\n", "green")) - if label: - print(colored(f"User ID: {label}", "cyan")) - if notes: - print(colored(f"Notes: {notes}", "cyan")) - tags = entry.get("tags", []) - if tags: - print(colored(f"Tags: {', '.join(tags)}", "cyan")) - print(colored(f"Fingerprint: {fingerprint}", "cyan")) - if self.secret_mode_enabled: - if copy_to_clipboard(priv_key, self.clipboard_clear_delay): - print( - colored( - f"[+] PGP key copied to clipboard. Will clear in {self.clipboard_clear_delay} seconds.", - "green", - ) - ) - else: - print(color_text(priv_key, "deterministic")) - except Exception as e: # pragma: no cover - best effort - logging.error(f"Error deriving PGP key: {e}", exc_info=True) - print(colored(f"Error: Failed to derive PGP key: {e}", "red")) - return - - if entry_type == EntryType.NOSTR.value: - label = entry.get("label", "") - notes = entry.get("notes", "") - try: - npub, nsec = self.entry_manager.get_nostr_key_pair( - index, self.parent_seed - ) - print(colored("\n[+] Retrieved Nostr Keys:\n", "green")) - print(colored(f"Label: {label}", "cyan")) - print(colored(f"npub: {npub}", "cyan")) - if self.secret_mode_enabled: - if copy_to_clipboard(nsec, self.clipboard_clear_delay): - print( - colored( - f"[+] nsec copied to clipboard. Will clear in {self.clipboard_clear_delay} seconds.", - "green", - ) - ) - else: - print(color_text(f"nsec: {nsec}", "deterministic")) - if notes: - print(colored(f"Notes: {notes}", "cyan")) - tags = entry.get("tags", []) - if tags: - print(colored(f"Tags: {', '.join(tags)}", "cyan")) - except Exception as e: # pragma: no cover - best effort - logging.error(f"Error deriving Nostr keys: {e}", exc_info=True) - print(colored(f"Error: Failed to derive Nostr keys: {e}", "red")) - return - - if entry_type == EntryType.KEY_VALUE.value: - label = entry.get("label", "") - value = entry.get("value", "") - notes = entry.get("notes", "") - archived = entry.get("archived", False) - print(colored(f"Retrieving value for key '{label}'.", "cyan")) - if notes: - print(colored(f"Notes: {notes}", "cyan")) - tags = entry.get("tags", []) - if tags: - print(colored(f"Tags: {', '.join(tags)}", "cyan")) - print( - colored( - f"Archived Status: {'Archived' if archived else 'Active'}", "cyan" - ) - ) - if self.secret_mode_enabled: - if copy_to_clipboard(value, self.clipboard_clear_delay): - print( - colored( - f"[+] Value copied to clipboard. Will clear in {self.clipboard_clear_delay} seconds.", - "green", - ) - ) - else: - print(color_text(f"Value: {value}", "deterministic")) - - custom_fields = entry.get("custom_fields", []) - if custom_fields: - print(colored("Additional Fields:", "cyan")) - hidden_fields = [] - for field in custom_fields: - f_label = field.get("label", "") - f_value = field.get("value", "") - if field.get("is_hidden"): - hidden_fields.append((f_label, f_value)) - print(colored(f" {f_label}: [hidden]", "cyan")) - else: - print(colored(f" {f_label}: {f_value}", "cyan")) - if hidden_fields: - show = input("Reveal hidden fields? (y/N): ").strip().lower() - if show == "y": - for f_label, f_value in hidden_fields: - if self.secret_mode_enabled: - if copy_to_clipboard( - f_value, self.clipboard_clear_delay - ): - print( - colored( - f"[+] {f_label} copied to clipboard. Will clear in {self.clipboard_clear_delay} seconds.", - "green", - ) - ) - else: - print(colored(f" {f_label}: {f_value}", "cyan")) - return - - if entry_type == EntryType.MANAGED_ACCOUNT.value: - label = entry.get("label", "") - notes = entry.get("notes", "") - archived = entry.get("archived", False) - fingerprint = entry.get("fingerprint", "") - print(colored(f"Managed account '{label}'.", "cyan")) - if notes: - print(colored(f"Notes: {notes}", "cyan")) - if fingerprint: - print(colored(f"Fingerprint: {fingerprint}", "cyan")) - tags = entry.get("tags", []) - if tags: - print(colored(f"Tags: {', '.join(tags)}", "cyan")) - print( - colored( - f"Archived Status: {'Archived' if archived else 'Active'}", "cyan" - ) - ) - action = ( - input( - "Enter 'r' to reveal seed, 'l' to load account, or press Enter to go back: " - ) - .strip() - .lower() - ) - if action == "r": - seed = self.entry_manager.get_managed_account_seed( - index, self.parent_seed - ) - if self.secret_mode_enabled: - if copy_to_clipboard(seed, self.clipboard_clear_delay): - print( - colored( - f"[+] Seed phrase copied to clipboard. Will clear in {self.clipboard_clear_delay} seconds.", - "green", - ) - ) - else: - print(color_text(seed, "deterministic")) - return - if action == "l": - self._suppress_entry_actions_menu = True - self.load_managed_account(index) - return - return - - # Default: PASSWORD - website_name = entry.get("label", entry.get("website")) - length = entry.get("length") - username = entry.get("username") - url = entry.get("url") - blacklisted = entry.get("archived", entry.get("blacklisted")) - notes = entry.get("notes", "") - - print( - colored( - f"Retrieving password for '{website_name}' with length {length}.", - "cyan", - ) - ) - if username: - print(colored(f"Username: {username}", "cyan")) - if url: - print(colored(f"URL: {url}", "cyan")) - if blacklisted: - self.notify( - "Warning: This password is archived and should not be used.", - level="WARNING", - ) - - password = self._generate_password_for_entry(entry, index, length) - - if password: - if self.secret_mode_enabled: - if copy_to_clipboard(password, self.clipboard_clear_delay): - print( - colored( - f"[+] Password for '{website_name}' copied to clipboard. Will clear in {self.clipboard_clear_delay} seconds.", - "green", - ) - ) - else: - print( - colored( - f"\n[+] Retrieved Password for {website_name}:\n", - "green", - ) - ) - print(color_text(f"Password: {password}", "deterministic")) - print(colored(f"Associated Username: {username or 'N/A'}", "cyan")) - print(colored(f"Associated URL: {url or 'N/A'}", "cyan")) - print( - colored( - f"Archived Status: {'Archived' if blacklisted else 'Active'}", - "cyan", - ) - ) - if notes: - print(colored(f"Notes: {notes}", "cyan")) - tags = entry.get("tags", []) - if tags: - print(colored(f"Tags: {', '.join(tags)}", "cyan")) - custom_fields = entry.get("custom_fields", []) - if custom_fields: - print(colored("Additional Fields:", "cyan")) - hidden_fields = [] - for field in custom_fields: - label = field.get("label", "") - value = field.get("value", "") - if field.get("is_hidden"): - hidden_fields.append((label, value)) - print(colored(f" {label}: [hidden]", "cyan")) - else: - print(colored(f" {label}: {value}", "cyan")) - if hidden_fields: - show = input("Reveal hidden fields? (y/N): ").strip().lower() - if show == "y": - for label, value in hidden_fields: - if self.secret_mode_enabled: - if copy_to_clipboard( - value, self.clipboard_clear_delay - ): - print( - colored( - f"[+] {label} copied to clipboard. Will clear in {self.clipboard_clear_delay} seconds.", - "green", - ) - ) - else: - print(colored(f" {label}: {value}", "cyan")) - else: - print(colored("Error: Failed to retrieve the password.", "red")) - return + self.display_service.display_sensitive_entry_info(entry, index) @pause_logging_for_ui def handle_retrieve_entry(self) -> None: