diff --git a/README.md b/README.md index 2355ea0..f1dce1e 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ SeedPass now uses the `portalocker` library for cross-platform file locking. No - **Export 2FA Codes:** Save all stored TOTP entries to an encrypted JSON file for use with other apps. - **Optional External Backup Location:** Configure a second directory where backups are automatically copied. - **Auto‑Lock on Inactivity:** Vault locks after a configurable timeout for additional security. +- **Secret Mode:** Copy retrieved passwords directly to your clipboard and automatically clear it after a delay. ## Prerequisites @@ -212,6 +213,14 @@ python src/main.py 3. Enter new values or press **Enter** to keep the existing settings. 4. The updated entry is saved back to your encrypted vault. +### Using Secret Mode + +When **Secret Mode** is enabled, SeedPass copies retrieved passwords directly to your clipboard instead of displaying them on screen. The clipboard clears automatically after the delay you choose. + +1. From the main menu open **Settings** and select **Toggle Secret Mode**. +2. Choose how many seconds to keep passwords on the clipboard. +3. Retrieve an entry and SeedPass will confirm the password was copied. + ### Managing Multiple Seeds diff --git a/landing/index.html b/landing/index.html index 5df7d41..d6568f0 100644 --- a/landing/index.html +++ b/landing/index.html @@ -72,6 +72,7 @@
When Secret Mode is enabled, retrieved passwords are copied directly to your clipboard instead of displayed. The clipboard clears automatically after a delay you set.
diff --git a/src/tests/test_secret_mode.py b/src/tests/test_secret_mode.py index f3a8b9a..193094f 100644 --- a/src/tests/test_secret_mode.py +++ b/src/tests/test_secret_mode.py @@ -80,3 +80,52 @@ def test_totp_display_secret_mode(monkeypatch, capsys): assert "123456" not in out assert "copied to clipboard" in out assert called == [("123456", 5)] + + +def test_password_retrieve_no_secret_mode(monkeypatch, capsys): + with TemporaryDirectory() as tmpdir: + tmp = Path(tmpdir) + pm, entry_mgr = setup_pm(tmp) + pm.secret_mode_enabled = False + entry_mgr.add_entry("example", 8) + + monkeypatch.setattr("builtins.input", lambda *a, **k: "0") + called = [] + monkeypatch.setattr( + "password_manager.manager.copy_to_clipboard", + lambda *a, **k: called.append((a, k)), + ) + + pm.handle_retrieve_entry() + out = capsys.readouterr().out + assert "Password:" in out + assert "copied to clipboard" not in out + assert called == [] + + +def test_totp_display_no_secret_mode(monkeypatch, capsys): + with TemporaryDirectory() as tmpdir: + tmp = Path(tmpdir) + pm, entry_mgr = setup_pm(tmp) + pm.secret_mode_enabled = False + entry_mgr.add_totp("Example", TEST_SEED) + + monkeypatch.setattr(pm.entry_manager, "get_totp_code", lambda *a, **k: "123456") + monkeypatch.setattr( + pm.entry_manager, "get_totp_time_remaining", lambda *a, **k: 30 + ) + monkeypatch.setattr( + "password_manager.manager.select.select", + lambda *a, **k: (_ for _ in ()).throw(KeyboardInterrupt()), + ) + called = [] + monkeypatch.setattr( + "password_manager.manager.copy_to_clipboard", + lambda *a, **k: called.append((a, k)), + ) + + pm.handle_display_totp_codes() + out = capsys.readouterr().out + assert "123456" in out + assert "copied to clipboard" not in out + assert called == []