From fa89313e6f17e268ce87ab272ea1bcdf5f493f39 Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Sat, 5 Jul 2025 13:16:49 +0300 Subject: [PATCH] [fix] bandit assert warnings (#3243) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: dni ⚡ --- lnbits/commands.py | 12 +-- lnbits/core/crud/extensions.py | 3 +- lnbits/core/crud/payments.py | 3 +- lnbits/core/crud/settings.py | 6 +- lnbits/core/crud/webpush.py | 3 +- lnbits/core/models/extensions.py | 8 +- lnbits/core/services/extensions.py | 14 +-- lnbits/core/services/lnurl.py | 3 +- lnbits/core/services/payments.py | 3 +- lnbits/core/services/settings.py | 3 +- lnbits/core/services/users.py | 3 +- lnbits/core/views/admin_api.py | 3 +- lnbits/core/views/api.py | 3 +- lnbits/core/views/auth_api.py | 77 ++++++++++----- lnbits/core/views/extension_api.py | 141 +++++++++++++--------------- lnbits/core/views/payment_api.py | 8 +- lnbits/core/views/user_api.py | 3 +- lnbits/middleware.py | 1 - lnbits/settings.py | 9 +- lnbits/utils/crypto.py | 3 +- lnbits/utils/nostr.py | 12 ++- lnbits/wallets/blink.py | 11 ++- lnbits/wallets/boltz.py | 3 +- lnbits/wallets/corelightningrest.py | 11 ++- lnbits/wallets/macaroon/macaroon.py | 3 +- lnbits/wallets/nwc.py | 3 +- poetry.lock | 63 +------------ pyproject.toml | 1 - 28 files changed, 196 insertions(+), 220 deletions(-) diff --git a/lnbits/commands.py b/lnbits/commands.py index 3c03c5c5..490a7096 100644 --- a/lnbits/commands.py +++ b/lnbits/commands.py @@ -320,9 +320,8 @@ async def extensions_list(): from lnbits.app import build_all_installed_extensions_list for ext in await build_all_installed_extensions_list(): - assert ( - ext.meta and ext.meta.installed_release - ), f"Extension {ext.id} has no installed_release" + if not ext.meta or not ext.meta.installed_release: + raise ValueError(f"Extension {ext.id} has no installed_release") click.echo(f" - {ext.id} ({ext.meta.installed_release.version})") @@ -608,9 +607,10 @@ async def update_extension( click.echo(f"Current '{extension}' version: {installed_ext.installed_version}.") - assert ( - installed_ext.meta and installed_ext.meta.installed_release - ), "Cannot find previously installed release. Please uninstall first." + if not installed_ext.meta or not installed_ext.meta.installed_release: + raise ValueError( + "Cannot find previously installed release. Please uninstall first." + ) release = await _select_release(extension, repo_index, source_repo) if not release: diff --git a/lnbits/core/crud/extensions.py b/lnbits/core/crud/extensions.py index 949d51ed..8bdef3ad 100644 --- a/lnbits/core/crud/extensions.py +++ b/lnbits/core/crud/extensions.py @@ -50,7 +50,8 @@ async def drop_extension_db(ext_id: str, conn: Optional[Connection] = None) -> N {"id": ext_id}, ) # Check that 'ext_id' is a valid extension id and not a malicious string - assert row, f"Extension '{ext_id}' db version cannot be found" + if not row: + raise Exception(f"Extension '{ext_id}' db version cannot be found") is_file_based_db = await Database.clean_ext_db_files(ext_id) if is_file_based_db: diff --git a/lnbits/core/crud/payments.py b/lnbits/core/crud/payments.py index e266d3e1..d6e82106 100644 --- a/lnbits/core/crud/payments.py +++ b/lnbits/core/crud/payments.py @@ -264,7 +264,8 @@ async def create_payment( # we don't allow the creation of the same invoice twice # note: this can be removed if the db uniqueness constraints are set appropriately previous_payment = await get_standalone_payment(checking_id, conn=conn) - assert previous_payment is None, "Payment already exists" + if previous_payment is not None: + raise ValueError("Payment already exists") extra = data.extra or {} payment = Payment( diff --git a/lnbits/core/crud/settings.py b/lnbits/core/crud/settings.py index 504ec0bf..fb803935 100644 --- a/lnbits/core/crud/settings.py +++ b/lnbits/core/crud/settings.py @@ -56,7 +56,8 @@ async def update_admin_settings( async def update_super_user(super_user: str) -> SuperSettings: await set_settings_field("super_user", super_user) settings = await get_super_settings() - assert settings, "updated super_user settings could not be retrieved" + if not settings: + raise ValueError("updated super_user settings could not be retrieved") return settings @@ -86,7 +87,8 @@ async def create_admin_settings(super_user: str, new_settings: dict) -> SuperSet await set_settings_field(key, value) settings = await get_super_settings() - assert settings, "created admin settings could not be retrieved" + if not settings: + raise ValueError("created admin settings could not be retrieved") return settings diff --git a/lnbits/core/crud/webpush.py b/lnbits/core/crud/webpush.py index a5fa5b58..3a812005 100644 --- a/lnbits/core/crud/webpush.py +++ b/lnbits/core/crud/webpush.py @@ -37,7 +37,8 @@ async def create_webpush_subscription( {"endpoint": endpoint, "user": user, "data": data, "host": host}, ) subscription = await get_webpush_subscription(endpoint, user) - assert subscription, "Newly created webpush subscription couldn't be retrieved" + if not subscription: + raise ValueError("Newly created webpush subscription couldn't be retrieved") return subscription diff --git a/lnbits/core/models/extensions.py b/lnbits/core/models/extensions.py index f57e8ae1..684488dc 100644 --- a/lnbits/core/models/extensions.py +++ b/lnbits/core/models/extensions.py @@ -211,7 +211,8 @@ class ExtensionRelease(BaseModel): self, amount: int | None = None ) -> ReleasePaymentInfo | None: url = f"{self.pay_link}?amount={amount}" if amount else self.pay_link - assert url, "Missing URL for payment info." + if not url: + raise ValueError("Missing URL for payment info.") try: async with httpx.AsyncClient() as client: resp = await client.get(url) @@ -376,9 +377,8 @@ class InstallableExtension(BaseModel): if ext_zip_file.is_file(): os.remove(ext_zip_file) try: - assert ( - self.meta and self.meta.installed_release - ), "installed_release is none." + if not self.meta or not self.meta.installed_release: + raise ValueError("No installed release.") self._restore_payment_info() diff --git a/lnbits/core/services/extensions.py b/lnbits/core/services/extensions.py index e5abb836..198a5323 100644 --- a/lnbits/core/services/extensions.py +++ b/lnbits/core/services/extensions.py @@ -26,10 +26,11 @@ async def install_extension(ext_info: InstallableExtension) -> Extension: ext_info.meta = ext_info.meta or ExtensionMeta() - if ext_info.meta.installed_release: - assert ( - ext_info.meta.installed_release.is_version_compatible - ), "Incompatible extension version" + if ( + ext_info.meta.installed_release + and not ext_info.meta.installed_release.is_version_compatible + ): + raise ValueError("Incompatible extension version") installed_ext = await get_installed_extension(ext_info.id) if installed_ext and installed_ext.meta: @@ -93,9 +94,8 @@ async def stop_extension_background_work(ext_id: str) -> bool: old_module = importlib.import_module(ext.module_name) stop_fn_name = f"{ext_id}_stop" - assert hasattr( - old_module, stop_fn_name - ), f"No stop function found for '{ext.module_name}'." + if not hasattr(old_module, stop_fn_name): + raise ValueError(f"No stop function found for '{ext.module_name}'.") stop_fn = getattr(old_module, stop_fn_name) if stop_fn: diff --git a/lnbits/core/services/lnurl.py b/lnbits/core/services/lnurl.py index 6592d4a0..619c3d05 100644 --- a/lnbits/core/services/lnurl.py +++ b/lnbits/core/services/lnurl.py @@ -136,7 +136,8 @@ async def perform_lnurlauth( headers = {"User-Agent": settings.user_agent} async with httpx.AsyncClient(headers=headers) as client: - assert key.verifying_key, "LNURLauth verifying_key does not exist" + if not key.verifying_key: + raise ValueError("LNURLauth verifying_key does not exist") check_callback_url(callback) r = await client.get( callback, diff --git a/lnbits/core/services/payments.py b/lnbits/core/services/payments.py index 46859471..0ddac429 100644 --- a/lnbits/core/services/payments.py +++ b/lnbits/core/services/payments.py @@ -66,7 +66,8 @@ async def pay_invoice( if settings.lnbits_only_allow_incoming_payments: raise PaymentError("Only incoming payments allowed.", status="failed") invoice = _validate_payment_request(payment_request, max_sat) - assert invoice.amount_msat + if not invoice.amount_msat: + raise ValueError("Missig invoice amount.") async with db.reuse_conn(conn) if conn else db.connect() as new_conn: amount_msat = invoice.amount_msat diff --git a/lnbits/core/services/settings.py b/lnbits/core/services/settings.py index 1ec66aba..ed7b8e85 100644 --- a/lnbits/core/services/settings.py +++ b/lnbits/core/services/settings.py @@ -19,7 +19,8 @@ async def check_webpush_settings(): vapid = Vapid() vapid.generate_keys() privkey = vapid.private_pem() - assert vapid.public_key, "VAPID public key does not exist" + if not vapid.public_key: + raise ValueError("VAPID public key does not exist") pubkey = b64urlencode( vapid.public_key.public_bytes( serialization.Encoding.X962, diff --git a/lnbits/core/services/users.py b/lnbits/core/services/users.py index 965112c8..71e010d7 100644 --- a/lnbits/core/services/users.py +++ b/lnbits/core/services/users.py @@ -80,7 +80,8 @@ async def create_user_account_no_ckeck( logger.error(f"Error enabeling default extension {ext_id}: {e}") user = await get_user_from_account(account) - assert user, "Cannot find user for account." + if not user: + raise ValueError("Cannot find user for account.") return user diff --git a/lnbits/core/views/admin_api.py b/lnbits/core/views/admin_api.py index af35a414..0cc33584 100644 --- a/lnbits/core/views/admin_api.py +++ b/lnbits/core/views/admin_api.py @@ -85,7 +85,8 @@ async def api_update_settings(data: UpdateSettings, user: User = Depends(check_a enqueue_notification(NotificationType.settings_update, {"username": user.username}) await update_admin_settings(data) admin_settings = await get_admin_settings(user.super_user) - assert admin_settings, "Updated admin settings not found." + if not admin_settings: + raise ValueError("Updated admin settings not found.") update_cached_settings(admin_settings.dict()) core_app_extra.register_new_ratelimiter() return {"status": "Success"} diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 313a3173..092c2548 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -124,7 +124,8 @@ async def api_lnurlscan( # noqa: C901 params.update(callback=url) # with k1 already in it lnurlauth_key = wallet.wallet.lnurlauth_key(domain) - assert lnurlauth_key.verifying_key + if not lnurlauth_key.verifying_key: + raise ValueError("LNURL auth key not found for this domain.") params.update(pubkey=lnurlauth_key.verifying_key.to_string("compressed").hex()) else: headers = {"User-Agent": settings.user_agent} diff --git a/lnbits/core/views/auth_api.py b/lnbits/core/views/auth_api.py index 92805323..65fd0c9b 100644 --- a/lnbits/core/views/auth_api.py +++ b/lnbits/core/views/auth_api.py @@ -191,12 +191,14 @@ async def api_create_user_api_token( data: ApiTokenRequest, user: User = Depends(check_user_exists), ) -> ApiTokenResponse: - assert data.expiration_time_minutes > 0, "Expiration time must be in the future." + if not data.expiration_time_minutes > 0: + raise ValueError("Expiration time must be in the future.") account = await get_account(user.id) if not account or not account.verify_password(data.password): raise HTTPException(HTTPStatus.UNAUTHORIZED, "Invalid credentials.") - assert account.username, "Username must be configured." + if not account.username: + raise ValueError("Username must be configured.") acls = await get_user_access_control_lists(user.id) acl = acls.get_acl_by_id(data.acl_id) @@ -223,7 +225,8 @@ async def api_delete_user_api_token( if not account or not account.verify_password(data.password): raise HTTPException(HTTPStatus.UNAUTHORIZED, "Invalid credentials.") - assert account.username, "Username must be configured." + if not account.username: + raise ValueError("Username must be configured.") acls = await get_user_access_control_lists(user.id) acl = acls.get_acl_by_id(data.acl_id) @@ -318,7 +321,7 @@ async def update_pubkey( payload: AccessTokenPayload = Depends(access_token_payload), ) -> Optional[User]: if data.user_id != user.id: - raise HTTPException(HTTPStatus.BAD_REQUEST, "Invalid user ID.") + raise ValueError("Invalid user ID.") _validate_auth_timeout(payload.auth_time) if ( @@ -326,7 +329,7 @@ async def update_pubkey( and data.pubkey != user.pubkey and await get_account_by_pubkey(data.pubkey) ): - raise HTTPException(HTTPStatus.BAD_REQUEST, "Public key already in use.") + raise ValueError("Public key already in use.") account = await get_account(user.id) if not account: @@ -344,7 +347,8 @@ async def update_password( payload: AccessTokenPayload = Depends(access_token_payload), ) -> Optional[User]: _validate_auth_timeout(payload.auth_time) - assert data.user_id == user.id, "Invalid user ID." + if data.user_id != user.id: + raise ValueError("Invalid user ID.") if ( data.username and user.username != data.username @@ -353,12 +357,15 @@ async def update_password( raise HTTPException(HTTPStatus.BAD_REQUEST, "Username already exists.") account = await get_account(user.id) - assert account, "Account not found." + if not account: + raise ValueError("Account not found.") # old accounts do not have a password if account.password_hash: - assert data.password_old, "Missing old password." - assert account.verify_password(data.password_old), "Invalid old password." + if not data.password_old: + raise ValueError("Missing old password.") + if not account.verify_password(data.password_old): + raise ValueError("Invalid old password.") account.username = data.username account.hash_password(data.password) @@ -376,8 +383,10 @@ async def reset_password(data: ResetUserPassword) -> JSONResponse: HTTPStatus.FORBIDDEN, "Auth by 'Username and Password' not allowed." ) - assert data.password == data.password_repeat, "Passwords do not match." - assert data.reset_key[:10].startswith("reset_key_"), "This is not a reset key." + if data.password != data.password_repeat: + raise ValueError("Passwords do not match.") + if not data.reset_key[:10].startswith("reset_key_"): + raise ValueError("This is not a reset key.") try: reset_key = base64.b64decode(data.reset_key[10:]).decode() @@ -385,12 +394,16 @@ async def reset_password(data: ResetUserPassword) -> JSONResponse: except Exception as exc: raise ValueError("Invalid reset key.") from exc - assert reset_data_json, "Cannot process reset key." + if not reset_data_json: + raise ValueError("Cannot process reset key.") action, user_id, request_time = json.loads(reset_data_json) - assert action, "Missing action." - assert user_id, "Missing user ID." - assert request_time, "Missing reset time." + if not action: + raise ValueError("Missing action.") + if not user_id: + raise ValueError("Missing user ID.") + if not request_time: + raise ValueError("Missing reset time.") _validate_auth_timeout(request_time) @@ -576,28 +589,40 @@ def _nostr_nip98_event(request: Request) -> dict: event = json.loads(event_json) except Exception as exc: logger.warning(exc) - assert event, "Nostr login event cannot be parsed." + if not event: + raise ValueError("Nostr login event cannot be parsed.") if not verify_event(event): raise HTTPException(HTTPStatus.BAD_REQUEST, "Nostr login event is not valid.") - assert event["kind"] == 27_235, "Invalid event kind." + if not event["kind"] == 27_235: + raise ValueError("Invalid event kind.") auth_threshold = settings.auth_credetials_update_threshold - assert ( - abs(time() - event["created_at"]) < auth_threshold - ), f"More than {auth_threshold} seconds have passed since the event was signed." + if not (abs(time() - event["created_at"]) < auth_threshold): + raise ValueError( + f"More than {auth_threshold} seconds have passed " + "since the event was signed." + ) + _check_nostr_event_tags(event) + + return event + + +def _check_nostr_event_tags(event: dict): method: Optional[str] = next((v for k, v in event["tags"] if k == "method"), None) - assert method, "Tag 'method' is missing." - assert method.upper() == "POST", "Invalid value for tag 'method'." + if not method: + raise ValueError("Tag 'method' is missing.") + if not method.upper() == "POST": + raise ValueError("Invalid value for tag 'method'.") url = next((v for k, v in event["tags"] if k == "u"), None) - assert url, "Tag 'u' for URL is missing." + if not url: + raise ValueError("Tag 'u' for URL is missing.") accepted_urls = [f"{u}/nostr" for u in settings.nostr_absolute_request_urls] - assert url in accepted_urls, f"Invalid value for tag 'u': '{url}'." - - return event + if url not in accepted_urls: + raise ValueError(f"Invalid value for tag 'u': '{url}'.") def _validate_auth_timeout(auth_time: Optional[int] = 0): diff --git a/lnbits/core/views/extension_api.py b/lnbits/core/views/extension_api.py index 6fdeceb2..0724d974 100644 --- a/lnbits/core/views/extension_api.py +++ b/lnbits/core/views/extension_api.py @@ -170,64 +170,52 @@ async def api_enable_extension( raise HTTPException( HTTPStatus.NOT_FOUND, f"Extension '{ext_id}' doesn't exist." ) - try: - logger.info(f"Enabling extension: {ext_id}.") - ext = await get_installed_extension(ext_id) - assert ext, f"Extension '{ext_id}' is not installed." - assert ext.active, f"Extension '{ext_id}' is not activated." - user_ext = await get_user_extension(user.id, ext_id) - if not user_ext: - user_ext = UserExtension(user=user.id, extension=ext_id, active=False) - await create_user_extension(user_ext) + logger.info(f"Enabling extension: {ext_id}.") + ext = await get_installed_extension(ext_id) + if not ext: + raise ValueError(f"Extension '{ext_id}' is not installed.") + if not ext.active: + raise ValueError(f"Extension '{ext_id}' is not activated.") - if user.admin or not ext.requires_payment: - user_ext.active = True - await update_user_extension(user_ext) - return SimpleStatus(success=True, message=f"Extension '{ext_id}' enabled.") + user_ext = await get_user_extension(user.id, ext_id) + if not user_ext: + user_ext = UserExtension(user=user.id, extension=ext_id, active=False) + await create_user_extension(user_ext) - if not (user_ext.extra and user_ext.extra.payment_hash_to_enable): - raise HTTPException( - HTTPStatus.PAYMENT_REQUIRED, f"Extension '{ext_id}' requires payment." - ) + if user.admin or not ext.requires_payment: + user_ext.active = True + await update_user_extension(user_ext) + return SimpleStatus(success=True, message=f"Extension '{ext_id}' enabled.") - if user_ext.is_paid: - user_ext.active = True - await update_user_extension(user_ext) - return SimpleStatus( - success=True, message=f"Paid extension '{ext_id}' enabled." - ) - - assert ( - ext.meta and ext.meta.pay_to_enable and ext.meta.pay_to_enable.wallet - ), f"Extension '{ext_id}' is missing payment wallet." - - payment_status = await check_transaction_status( - wallet_id=ext.meta.pay_to_enable.wallet, - payment_hash=user_ext.extra.payment_hash_to_enable, + if not (user_ext.extra and user_ext.extra.payment_hash_to_enable): + raise HTTPException( + HTTPStatus.PAYMENT_REQUIRED, f"Extension '{ext_id}' requires payment." ) - if not payment_status.paid: - raise HTTPException( - HTTPStatus.PAYMENT_REQUIRED, - f"Invoice generated but not paid for enabeling extension '{ext_id}'.", - ) - + if user_ext.is_paid: user_ext.active = True - user_ext.extra.paid_to_enable = True await update_user_extension(user_ext) return SimpleStatus(success=True, message=f"Paid extension '{ext_id}' enabled.") - except AssertionError as exc: - raise HTTPException(HTTPStatus.BAD_REQUEST, str(exc)) from exc - except HTTPException as exc: - raise exc from exc - except Exception as exc: - logger.warning(exc) + if not ext.meta or not ext.meta.pay_to_enable or not ext.meta.pay_to_enable.wallet: + raise ValueError(f"Extension '{ext_id}' is missing payment wallet.") + + payment_status = await check_transaction_status( + wallet_id=ext.meta.pay_to_enable.wallet, + payment_hash=user_ext.extra.payment_hash_to_enable, + ) + + if not payment_status.paid: raise HTTPException( - status_code=HTTPStatus.INTERNAL_SERVER_ERROR, - detail=(f"Failed to enable '{ext_id}' "), - ) from exc + HTTPStatus.PAYMENT_REQUIRED, + f"Invoice generated but not paid for enabeling extension '{ext_id}'.", + ) + + user_ext.active = True + user_ext.extra.paid_to_enable = True + await update_user_extension(user_ext) + return SimpleStatus(success=True, message=f"Paid extension '{ext_id}' enabled.") @extension_router.put("/{ext_id}/disable") @@ -255,7 +243,8 @@ async def api_activate_extension(ext_id: str) -> SimpleStatus: logger.info(f"Activating extension: '{ext_id}'.") ext = await get_valid_extension(ext_id) - assert ext, f"Extension '{ext_id}' doesn't exist." + if not ext: + raise ValueError(f"Extension '{ext_id}' doesn't exist.") await activate_extension(ext) return SimpleStatus(success=True, message=f"Extension '{ext_id}' activated.") @@ -274,7 +263,8 @@ async def api_deactivate_extension(ext_id: str) -> SimpleStatus: logger.info(f"Deactivating extension: '{ext_id}'.") ext = await get_valid_extension(ext_id) - assert ext, f"Extension '{ext_id}' doesn't exist." + if not ext: + raise ValueError(f"Extension '{ext_id}' doesn't exist.") await deactivate_extension(ext_id) return SimpleStatus(success=True, message=f"Extension '{ext_id}' deactivated.") @@ -354,40 +344,35 @@ async def get_extension_releases(ext_id: str) -> list[ExtensionRelease]: async def get_pay_to_install_invoice( ext_id: str, data: CreateExtension ) -> ReleasePaymentInfo: - try: - assert ( - ext_id == data.ext_id - ), f"Wrong extension id. Expected {ext_id}, but got {data.ext_id}" - assert data.cost_sats, "A non-zero amount must be specified." - release = await InstallableExtension.get_extension_release( - data.ext_id, data.source_repo, data.archive, data.version + if ext_id != data.ext_id: + raise ValueError( + f"Wrong extension id. Expected {ext_id}, but got {data.ext_id}" ) - assert release, "Release not found." - assert release.pay_link, "Pay link not found for release." + if not data.cost_sats: + raise ValueError("A non-zero amount must be specified.") + release = await InstallableExtension.get_extension_release( + data.ext_id, data.source_repo, data.archive, data.version + ) + if not release: + raise ValueError("Release not found.") + if not release.pay_link: + raise ValueError("Pay link not found for release.") - payment_info = await release.fetch_release_payment_info(data.cost_sats) + payment_info = await release.fetch_release_payment_info(data.cost_sats) - assert payment_info and payment_info.payment_request, "Cannot request invoice." - invoice = bolt11_decode(payment_info.payment_request) + if not (payment_info and payment_info.payment_request): + raise ValueError("Cannot request invoice.") + invoice = bolt11_decode(payment_info.payment_request) - assert invoice.amount_msat is not None, "Invoic amount is missing." - invoice_amount = int(invoice.amount_msat / 1000) - assert ( - invoice_amount == data.cost_sats - ), f"Wrong invoice amount: {invoice_amount}." - assert ( - payment_info.payment_hash == invoice.payment_hash - ), "Wrong invoice payment hash." + if invoice.amount_msat is None: + raise ValueError("Invoic amount is missing.") + invoice_amount = int(invoice.amount_msat / 1000) + if invoice_amount != data.cost_sats: + raise ValueError(f"Wrong invoice amount: {invoice_amount}.") + if payment_info.payment_hash != invoice.payment_hash: + raise ValueError("Wrong invoice payment hash.") - return payment_info - - except AssertionError as exc: - raise HTTPException(HTTPStatus.BAD_REQUEST, str(exc)) from exc - except Exception as exc: - logger.warning(exc) - raise HTTPException( - HTTPStatus.INTERNAL_SERVER_ERROR, "Cannot request invoice" - ) from exc + return payment_info @extension_router.put("/{ext_id}/invoice/enable") diff --git a/lnbits/core/views/payment_api.py b/lnbits/core/views/payment_api.py index eea74a42..77729051 100644 --- a/lnbits/core/views/payment_api.py +++ b/lnbits/core/views/payment_api.py @@ -349,11 +349,11 @@ async def api_payments_pay_lnurl( extra["fiat_currency"] = data.unit extra["fiat_amount"] = data.amount / 1000 if data.internal_memo is not None: - assert ( - len(data.internal_memo) <= 512 - ), "Internal memo must be 512 characters or less." + if len(data.internal_memo) > 512: + raise ValueError("Internal memo must be 512 characters or less.") extra["internal_memo"] = data.internal_memo - assert data.description is not None, "description is required" + if data.description is None: + raise ValueError("Description is required") payment = await pay_invoice( wallet_id=wallet.wallet.id, diff --git a/lnbits/core/views/user_api.py b/lnbits/core/views/user_api.py index 0f445c69..1ae99e3f 100644 --- a/lnbits/core/views/user_api.py +++ b/lnbits/core/views/user_api.py @@ -187,7 +187,8 @@ async def api_users_reset_password(user_id: str) -> str: reset_data = ["reset", user_id, int(time.time())] reset_data_json = json.dumps(reset_data, separators=(",", ":"), ensure_ascii=False) reset_key = encrypt_internal_message(reset_data_json) - assert reset_key, "Cannot generate reset key." + if not reset_key: + raise ValueError("Cannot generate reset key.") reset_key_b64 = base64.b64encode(reset_key.encode()).decode() return f"reset_key_{reset_key_b64}" diff --git a/lnbits/middleware.py b/lnbits/middleware.py index 76824a4d..350ddc3c 100644 --- a/lnbits/middleware.py +++ b/lnbits/middleware.py @@ -131,7 +131,6 @@ class AuditMiddleware(BaseHTTPMiddleware): try: response = await call_next(request) - assert response return response finally: if request_details: diff --git a/lnbits/settings.py b/lnbits/settings.py index 6828b48a..3654514c 100644 --- a/lnbits/settings.py +++ b/lnbits/settings.py @@ -201,10 +201,11 @@ class InstalledExtensionsSettings(LNbitsSettings): if r.find_in_conflict(ext_redirect_paths) } - assert len(existing_redirects) == 0, ( - f"Cannot redirect for extension '{ext_id}'." - f" Already mapped by {existing_redirects}." - ) + if len(existing_redirects) != 0: + raise ValueError( + f"Cannot redirect for extension '{ext_id}'." + f" Already mapped by {existing_redirects}." + ) self._remove_extension_redirects(ext_id) self.lnbits_extensions_redirects += ext_redirect_paths diff --git a/lnbits/utils/crypto.py b/lnbits/utils/crypto.py index f093181d..0876dac0 100644 --- a/lnbits/utils/crypto.py +++ b/lnbits/utils/crypto.py @@ -66,7 +66,8 @@ class AESCipher: self, salt: bytes, output_len: int = 32 + 16 ) -> tuple[bytes, bytes]: # extended from https://gist.github.com/gsakkis/4546068 - assert len(salt) == 8, "Salt must be 8 bytes" + if len(salt) != 8: + raise ValueError("Salt must be 8 bytes") data = self.key + salt key = md5(data).digest() final_key = key diff --git a/lnbits/utils/nostr.py b/lnbits/utils/nostr.py index 953996fa..b6218446 100644 --- a/lnbits/utils/nostr.py +++ b/lnbits/utils/nostr.py @@ -175,14 +175,17 @@ def normalize_private_key(key: str) -> str: def normalize_bech32_key(hrp: str, key: str) -> str: if key.startswith(hrp): _, decoded_data = bech32_decode(key) - assert decoded_data, f"Key is not valid {hrp}." + if not decoded_data: + raise ValueError(f"Key is not valid {hrp}.") decoded_data_bits = convertbits(decoded_data, 5, 8, False) - assert decoded_data_bits, f"Key is not valid {hrp}." + if not decoded_data_bits: + raise ValueError(f"Key is not valid {hrp}.") return bytes(decoded_data_bits).hex() - assert len(key) == 64, "Key has wrong length." + if len(key) != 64: + raise ValueError("Key has wrong length.") try: int(key, 16) except Exception as exc: @@ -203,7 +206,8 @@ def hex_to_npub(hex_pubkey: str) -> str: normalize_public_key(hex_pubkey) pubkey_bytes = bytes.fromhex(hex_pubkey) bits = convertbits(pubkey_bytes, 8, 5, True) - assert bits + if not bits: + raise ValueError("Invalid Nostr public key.") return bech32_encode("npub", bits) diff --git a/lnbits/wallets/blink.py b/lnbits/wallets/blink.py index 6175db9a..c6f733eb 100644 --- a/lnbits/wallets/blink.py +++ b/lnbits/wallets/blink.py @@ -252,7 +252,8 @@ class BlinkWallet(Wallet): response = await self._graphql_query(data) response_data = response.get("data") - assert response_data is not None + if response_data is None: + raise ValueError("No data found in response.") txs_data = ( response_data.get("me", {}) .get("defaultAccount", {}) @@ -260,7 +261,8 @@ class BlinkWallet(Wallet): .get("transactionsByPaymentHash", []) ) tx_data = next((t for t in txs_data if t.get("direction") == "SEND"), None) - assert tx_data, "No SEND data found." + if not tx_data: + raise ValueError("No SEND data found.") fee = tx_data.get("settlementFee") preimage = tx_data.get("settlementVia", {}).get("preImage") status = tx_data.get("status") @@ -284,9 +286,8 @@ class BlinkWallet(Wallet): await ws.send(json.dumps(self.ws_auth)) confirmation = await ws.recv() ack = json.loads(confirmation) - assert ( - ack.get("type") == "connection_ack" - ), "Websocket connection not acknowledged." + if ack.get("type") != "connection_ack": + raise ValueError("Websocket connection not acknowledged.") logger.info("Websocket connection acknowledged.") subscription_req = { diff --git a/lnbits/wallets/boltz.py b/lnbits/wallets/boltz.py index 87d236d1..578cf08c 100644 --- a/lnbits/wallets/boltz.py +++ b/lnbits/wallets/boltz.py @@ -142,7 +142,8 @@ class BoltzWallet(Wallet): pair_info = await self.rpc.GetPairInfo(pair_request, metadata=self.metadata) invoice = decode(bolt11) - assert invoice.amount_msat, "amountless invoice" + if not invoice.amount_msat: + raise ValueError("amountless invoice") service_fee: float = invoice.amount_msat * pair_info.fees.percentage / 100 estimate = service_fee + pair_info.fees.miner_fees * 1000 if estimate > fee_limit_msat: diff --git a/lnbits/wallets/corelightningrest.py b/lnbits/wallets/corelightningrest.py index fe6ded01..09597ddf 100644 --- a/lnbits/wallets/corelightningrest.py +++ b/lnbits/wallets/corelightningrest.py @@ -314,11 +314,12 @@ class CoreLightningRestWallet(Wallet): ) paid_invoice = r.json() logger.trace(f"paid invoice: {paid_invoice}") - assert self.statuses[ - paid_invoice["invoices"][0]["status"] - ], "streamed invoice not paid" - assert "invoices" in paid_invoice, "no invoices in response" - assert len(paid_invoice["invoices"]), "no invoices in response" + if not self.statuses[paid_invoice["invoices"][0]["status"]]: + raise ValueError("streamed invoice not paid") + if "invoices" not in paid_invoice: + raise ValueError("no invoices in response") + if not len(paid_invoice["invoices"]): + raise ValueError("no invoices in response") yield paid_invoice["invoices"][0]["payment_hash"] except Exception as exc: diff --git a/lnbits/wallets/macaroon/macaroon.py b/lnbits/wallets/macaroon/macaroon.py index e445677a..811ff3f2 100644 --- a/lnbits/wallets/macaroon/macaroon.py +++ b/lnbits/wallets/macaroon/macaroon.py @@ -22,7 +22,8 @@ def load_macaroon( aes = AESCipher(key.encode()) return aes.decrypt(encrypted_macaroon) - assert macaroon, "macaroon must be set here" + if not macaroon: + raise ValueError("macaroon must be set here") # if the macaroon is a file path, load it and return hex version if macaroon.split(".")[-1] == "macaroon": diff --git a/lnbits/wallets/nwc.py b/lnbits/wallets/nwc.py index b15f6a13..eff1a4ef 100644 --- a/lnbits/wallets/nwc.py +++ b/lnbits/wallets/nwc.py @@ -309,7 +309,8 @@ class NWCConnection: self.account_private_key = secp256k1.PrivateKey(bytes.fromhex(secret)) self.account_private_key_hex = secret self.account_public_key = self.account_private_key.pubkey - assert self.account_public_key + if not self.account_public_key: + raise ValueError("Missing account public key") self.account_public_key_hex = self.account_public_key.serialize().hex()[2:] # Extract service key (used for encryption to identify the nwc service provider) diff --git a/poetry.lock b/poetry.lock index 7dee5335..4c074ca5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -363,31 +363,6 @@ files = [ {file = "backports_datetime_fromisoformat-2.0.3.tar.gz", hash = "sha256:b58edc8f517b66b397abc250ecc737969486703a66eb97e01e6d51291b1a139d"}, ] -[[package]] -name = "bandit" -version = "1.8.5" -description = "Security oriented static analyser for python code." -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "bandit-1.8.5-py3-none-any.whl", hash = "sha256:cb2e57524e99e33ced48833c6cc9c12ac78ae970bb6a450a83c4b506ecc1e2f9"}, - {file = "bandit-1.8.5.tar.gz", hash = "sha256:db812e9c39b8868c0fed5278b77fffbbaba828b4891bc80e34b9c50373201cfd"}, -] - -[package.dependencies] -colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} -PyYAML = ">=5.3.1" -rich = "*" -stevedore = ">=1.20.0" - -[package.extras] -baseline = ["GitPython (>=3.1.30)"] -sarif = ["jschema-to-python (>=1.2.3)", "sarif-om (>=1.0.4)"] -test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)"] -toml = ["tomli (>=1.1.0) ; python_version < \"3.11\""] -yaml = ["PyYAML"] - [[package]] name = "base58" version = "2.1.1" @@ -2119,7 +2094,7 @@ version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] +groups = ["main"] files = [ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, @@ -2236,7 +2211,7 @@ version = "0.1.2" description = "Markdown URL utilities" optional = false python-versions = ">=3.7" -groups = ["main", "dev"] +groups = ["main"] files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, @@ -2606,21 +2581,6 @@ files = [ {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] -[[package]] -name = "pbr" -version = "6.1.1" -description = "Python Build Reasonableness" -optional = false -python-versions = ">=2.6" -groups = ["dev"] -files = [ - {file = "pbr-6.1.1-py2.py3-none-any.whl", hash = "sha256:38d4daea5d9fa63b3f626131b9d34947fd0c8be9b05a29276870580050a25a76"}, - {file = "pbr-6.1.1.tar.gz", hash = "sha256:93ea72ce6989eb2eed99d0f75721474f69ad88128afdef5ac377eb797c4bf76b"}, -] - -[package.dependencies] -setuptools = "*" - [[package]] name = "platformdirs" version = "4.3.8" @@ -3384,7 +3344,7 @@ version = "14.0.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.8.0" -groups = ["main", "dev"] +groups = ["main"] files = [ {file = "rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0"}, {file = "rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725"}, @@ -3793,21 +3753,6 @@ anyio = ">=3.4.0,<5" [package.extras] full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] -[[package]] -name = "stevedore" -version = "5.4.1" -description = "Manage dynamic plugins for Python applications" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "stevedore-5.4.1-py3-none-any.whl", hash = "sha256:d10a31c7b86cba16c1f6e8d15416955fc797052351a56af15e608ad20811fcfe"}, - {file = "stevedore-5.4.1.tar.gz", hash = "sha256:3135b5ae50fe12816ef291baff420acb727fcd356106e3e9cbfa9e5985cd6f4b"}, -] - -[package.dependencies] -pbr = ">=2.0.0" - [[package]] name = "tlv8" version = "0.10.0" @@ -4475,4 +4420,4 @@ liquid = ["wallycore"] [metadata] lock-version = "2.1" python-versions = "~3.12 | ~3.11 | ~3.10" -content-hash = "17a61e0f0d45c02d99c398f8ddfd68f7bd55a04183cad7d615e170813be43348" +content-hash = "53f582a8079540033939ccd1bbd93b8ec1e8190ee26be0c0b8d64d57edb5cdac" diff --git a/pyproject.toml b/pyproject.toml index 8672602a..7eb79e93 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -70,7 +70,6 @@ breez = ["breez-sdk", "breez-sdk-liquid"] liquid = ["wallycore"] [tool.poetry.group.dev.dependencies] -bandit = "^1.8.5" black = "^25.1.0" mypy = "^1.11.2" types-protobuf = "^6.30.2.20250516"