From a79f3c890575793d96aa658dbf542ad9af938492 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Wed, 9 Jul 2025 15:54:26 -0400 Subject: [PATCH] Add checksum CLI and API utilities --- docs/advanced_cli.md | 4 ++++ docs/api_reference.md | 2 ++ src/seedpass/api.py | 18 ++++++++++++++++++ src/seedpass/cli.py | 14 ++++++++++++++ src/tests/test_api_new_endpoints.py | 20 ++++++++++++++++++++ src/tests/test_typer_cli.py | 28 ++++++++++++++++++++++++++++ 6 files changed, 86 insertions(+) diff --git a/docs/advanced_cli.md b/docs/advanced_cli.md index 5a8c0d6..a8e15e5 100644 --- a/docs/advanced_cli.md +++ b/docs/advanced_cli.md @@ -106,6 +106,8 @@ Miscellaneous helper commands. | Action | Command | Examples | | :--- | :--- | :--- | | Generate a password | `util generate-password` | `seedpass util generate-password --length 24` | +| Verify script checksum | `util verify-checksum` | `seedpass util verify-checksum` | +| Update script checksum | `util update-checksum` | `seedpass util update-checksum` | ### API Commands @@ -170,6 +172,8 @@ Code: 123456 ### `util` Commands - **`seedpass util generate-password`** – Generate a strong password of the requested length. +- **`seedpass util verify-checksum`** – Verify the SeedPass script checksum. +- **`seedpass util update-checksum`** – Regenerate the script checksum file. --- diff --git a/docs/api_reference.md b/docs/api_reference.md index 225b0c5..4dedac7 100644 --- a/docs/api_reference.md +++ b/docs/api_reference.md @@ -28,6 +28,8 @@ Keep this token secret. Every request must include it in the `Authorization` hea - `DELETE /api/v1/fingerprint/{fp}` – Remove a fingerprint. - `POST /api/v1/fingerprint/select` – Switch the active fingerprint. - `GET /api/v1/nostr/pubkey` – Fetch the Nostr public key for the active seed. +- `POST /api/v1/checksum/verify` – Verify the checksum of the running script. +- `POST /api/v1/checksum/update` – Update the stored script checksum. - `POST /api/v1/change-password` – Change the master password for the active profile. - `POST /api/v1/shutdown` – Stop the server gracefully. diff --git a/src/seedpass/api.py b/src/seedpass/api.py index e426ff7..a3231af 100644 --- a/src/seedpass/api.py +++ b/src/seedpass/api.py @@ -311,6 +311,24 @@ def get_nostr_pubkey(authorization: str | None = Header(None)) -> Any: return {"npub": _pm.nostr_client.key_manager.get_npub()} +@app.post("/api/v1/checksum/verify") +def verify_checksum(authorization: str | None = Header(None)) -> dict[str, str]: + """Verify the SeedPass script checksum.""" + _check_token(authorization) + assert _pm is not None + _pm.handle_verify_checksum() + return {"status": "ok"} + + +@app.post("/api/v1/checksum/update") +def update_checksum(authorization: str | None = Header(None)) -> dict[str, str]: + """Regenerate the script checksum file.""" + _check_token(authorization) + assert _pm is not None + _pm.handle_update_script_checksum() + return {"status": "ok"} + + @app.post("/api/v1/change-password") def change_password(authorization: str | None = Header(None)) -> dict[str, str]: """Change the master password for the active profile.""" diff --git a/src/seedpass/cli.py b/src/seedpass/cli.py index 7b2a308..66a4359 100644 --- a/src/seedpass/cli.py +++ b/src/seedpass/cli.py @@ -430,6 +430,20 @@ def generate_password(ctx: typer.Context, length: int = 24) -> None: typer.echo(password) +@util_app.command("verify-checksum") +def verify_checksum(ctx: typer.Context) -> None: + """Verify the SeedPass script checksum.""" + pm = _get_pm(ctx) + pm.handle_verify_checksum() + + +@util_app.command("update-checksum") +def update_checksum(ctx: typer.Context) -> None: + """Regenerate the script checksum file.""" + pm = _get_pm(ctx) + pm.handle_update_script_checksum() + + @api_app.command("start") def api_start(ctx: typer.Context, host: str = "127.0.0.1", port: int = 8000) -> None: """Start the SeedPass API server.""" diff --git a/src/tests/test_api_new_endpoints.py b/src/tests/test_api_new_endpoints.py index f4ab8b5..5606c0a 100644 --- a/src/tests/test_api_new_endpoints.py +++ b/src/tests/test_api_new_endpoints.py @@ -141,3 +141,23 @@ def test_fingerprint_endpoints(client): assert res.status_code == 200 assert res.json() == {"status": "ok"} assert calls.get("select") == "xyz" + + +def test_checksum_endpoints(client): + cl, token = client + calls = {} + + api._pm.handle_verify_checksum = lambda: calls.setdefault("verify", True) + api._pm.handle_update_script_checksum = lambda: calls.setdefault("update", True) + + headers = {"Authorization": f"Bearer {token}"} + + res = cl.post("/api/v1/checksum/verify", headers=headers) + assert res.status_code == 200 + assert res.json() == {"status": "ok"} + assert calls.get("verify") is True + + res = cl.post("/api/v1/checksum/update", headers=headers) + assert res.status_code == 200 + assert res.json() == {"status": "ok"} + assert calls.get("update") is True diff --git a/src/tests/test_typer_cli.py b/src/tests/test_typer_cli.py index db96863..d64c06c 100644 --- a/src/tests/test_typer_cli.py +++ b/src/tests/test_typer_cli.py @@ -334,3 +334,31 @@ def test_entry_unarchive(monkeypatch): assert result.exit_code == 0 assert "4" in result.stdout assert called["id"] == 4 + + +def test_verify_checksum_command(monkeypatch): + called = {} + + pm = SimpleNamespace( + handle_verify_checksum=lambda: called.setdefault("called", True), + handle_update_script_checksum=lambda: None, + select_fingerprint=lambda fp: None, + ) + monkeypatch.setattr(cli, "PasswordManager", lambda: pm) + result = runner.invoke(app, ["util", "verify-checksum"]) + assert result.exit_code == 0 + assert called.get("called") is True + + +def test_update_checksum_command(monkeypatch): + called = {} + + pm = SimpleNamespace( + handle_verify_checksum=lambda: None, + handle_update_script_checksum=lambda: called.setdefault("called", True), + select_fingerprint=lambda fp: None, + ) + monkeypatch.setattr(cli, "PasswordManager", lambda: pm) + result = runner.invoke(app, ["util", "update-checksum"]) + assert result.exit_code == 0 + assert called.get("called") is True