Add dependency scanning and optional dependency checks

This commit is contained in:
thePR0M3TH3AN
2025-08-05 21:04:50 -04:00
parent c2d80aa438
commit 68eaa34d76
5 changed files with 90 additions and 56 deletions

View File

@@ -83,10 +83,8 @@ jobs:
pip-compile --generate-hashes --output-file=requirements.lock src/requirements.txt
git diff --exit-code requirements.lock
pip install --require-hashes -r requirements.lock
- name: Run pip-audit
run: |
pip install pip-audit
pip-audit -r requirements.lock --ignore-vuln GHSA-wj6h-64fc-37mp
- name: Run dependency scan
run: scripts/dependency_scan.sh --ignore-vuln GHSA-wj6h-64fc-37mp
- name: Determine stress args
shell: bash
run: |

9
scripts/dependency_scan.sh Executable file
View File

@@ -0,0 +1,9 @@
#!/usr/bin/env bash
set -euo pipefail
# Run pip-audit against the pinned requirements
if ! command -v pip-audit >/dev/null 2>&1; then
python -m pip install --quiet pip-audit
fi
pip-audit -r requirements.lock "$@"

View File

@@ -18,6 +18,7 @@ from colorama import init as colorama_init
from termcolor import colored
from utils.color_scheme import color_text
import traceback
import importlib
from seedpass.core.manager import PasswordManager
from nostr.client import NostrClient
@@ -38,6 +39,25 @@ from local_bip85.bip85 import Bip85Error
colorama_init()
OPTIONAL_DEPENDENCIES = {
"pyperclip": "clipboard support for secret mode",
"qrcode": "QR code generation for TOTP setup",
"toga": "desktop GUI features",
}
def _warn_missing_optional_dependencies() -> None:
"""Log warnings for any optional packages that are not installed."""
for module, feature in OPTIONAL_DEPENDENCIES.items():
try:
importlib.import_module(module)
except ModuleNotFoundError:
logging.warning(
"Optional dependency '%s' is not installed; %s will be unavailable.",
module,
feature,
)
def load_global_config() -> dict:
"""Load configuration from ~/.seedpass/config.toml if present."""
@@ -1205,6 +1225,7 @@ def main(argv: list[str] | None = None, *, fingerprint: str | None = None) -> in
Optional seed profile fingerprint to select automatically.
"""
configure_logging()
_warn_missing_optional_dependencies()
initialize_app()
logger = logging.getLogger(__name__)
logger.info("Starting SeedPass Password Manager")

View File

@@ -22,19 +22,21 @@ pgpy==0.6.0
pyotp>=2.8.0
freezegun
pyperclip
qrcode>=8.2
typer>=0.12.3
fastapi>=0.116.1
uvicorn>=0.35.0
starlette>=0.47.2
httpx>=0.28.1
requests>=2.32
python-multipart>=0.0.20
PyJWT
orjson
argon2-cffi
toga-core>=0.5.2
pillow
toga-dummy>=0.5.2 # for headless GUI tests
slowapi
# Optional dependencies - install as needed for additional features
pyperclip # Clipboard support for secret mode
qrcode>=8.2 # Generate QR codes for TOTP setup
fastapi>=0.116.1 # API server
uvicorn>=0.35.0 # API server
starlette>=0.47.2 # API server
httpx>=0.28.1 # API server
requests>=2.32 # API server
python-multipart>=0.0.20 # API server file uploads
PyJWT # JWT authentication for API server
orjson # Fast JSON serialization for API server
argon2-cffi # Password hashing for API server
toga-core>=0.5.2 # Desktop GUI
pillow # Image support for GUI
toga-dummy>=0.5.2 # Headless GUI tests
slowapi # Rate limiting for API server

View File

@@ -1,47 +1,51 @@
# utils/__init__.py
"""Utility package exports and optional feature handling."""
import logging
import traceback
logger = logging.getLogger(__name__)
try:
from .file_lock import exclusive_lock, shared_lock
from .key_derivation import (
derive_key_from_password,
derive_key_from_parent_seed,
derive_index_key,
derive_totp_secret,
EncryptionMode,
DEFAULT_ENCRYPTION_MODE,
TOTP_PURPOSE,
)
from .checksum import (
calculate_checksum,
verify_checksum,
json_checksum,
canonical_json_dumps,
initialize_checksum,
update_checksum_file,
)
from .password_prompt import prompt_for_password
from .seed_prompt import masked_input, prompt_seed_words
from .input_utils import timed_input
from .memory_protection import InMemorySecret
from .clipboard import copy_to_clipboard
from .terminal_utils import (
clear_screen,
pause,
clear_and_print_fingerprint,
clear_header_with_notification,
)
from .atomic_write import atomic_write
from .file_lock import exclusive_lock, shared_lock
from .key_derivation import (
derive_key_from_password,
derive_key_from_parent_seed,
derive_index_key,
derive_totp_secret,
EncryptionMode,
DEFAULT_ENCRYPTION_MODE,
TOTP_PURPOSE,
)
from .checksum import (
calculate_checksum,
verify_checksum,
json_checksum,
canonical_json_dumps,
initialize_checksum,
update_checksum_file,
)
from .password_prompt import prompt_for_password
from .seed_prompt import masked_input, prompt_seed_words
from .input_utils import timed_input
from .memory_protection import InMemorySecret
from .terminal_utils import (
clear_screen,
pause,
clear_and_print_fingerprint,
clear_header_with_notification,
)
from .atomic_write import atomic_write
# Optional clipboard support
try: # pragma: no cover - exercised when dependency missing
from .clipboard import copy_to_clipboard
except Exception as exc: # pragma: no cover - executed only if pyperclip missing
def copy_to_clipboard(*_args, **_kwargs):
"""Stub when clipboard support is unavailable."""
logger.warning("Clipboard support unavailable: %s", exc)
return False
if logger.isEnabledFor(logging.DEBUG):
logger.info("Modules imported successfully.")
except Exception as e:
if logger.isEnabledFor(logging.DEBUG):
logger.error(f"Failed to import one or more modules: {e}", exc_info=True)
__all__ = [
"derive_key_from_password",