Skip to content

Commit bcb7406

Browse files
committed
lnurl: only sanitize untrusted LNURL errors
it is not neccessary to sanitize all LNURLErrors as most are just source strings.
1 parent 313c8a1 commit bcb7406

File tree

1 file changed

+19
-11
lines changed

1 file changed

+19
-11
lines changed

electrum/lnurl.py

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,30 @@
1010

1111
import aiohttp.client_exceptions
1212

13-
from electrum import segwit_addr
13+
from electrum import segwit_addr, util
1414
from electrum.segwit_addr import bech32_decode, Encoding, convertbits, bech32_encode
1515
from electrum.lnaddr import LnDecodeException, LnEncodeException
1616
from electrum.network import Network
1717
from electrum.logging import get_logger
18+
from electrum.i18n import _
1819

1920

2021
_logger = get_logger(__name__)
2122

2223

23-
class LNURLError(Exception):
24-
def __init__(self, message="", *args):
25-
# error messages are returned by the LNURL server, some services could try to trick
26-
# users into doing something by sending a malicious error message
27-
modified_message = f"[DO NOT TRUST THIS MESSAGE]:\n{message}"
28-
super().__init__(modified_message, *args)
24+
class LNURLError(Exception): pass
25+
26+
class UntrustedLNURLError(LNURLError):
27+
def __init__(self, message=""):
28+
# use if error messages are returned by the LNURL server,
29+
# some services could try to trick users into doing something
30+
# by sending a malicious error message
31+
if message:
32+
message = (
33+
f"{_('[DO NOT TRUST THIS MESSAGE]:')}\n"
34+
f"{util.error_text_str_to_safe_str(message)}"
35+
)
36+
super().__init__(message)
2937

3038

3139
def decode_lnurl(lnurl: str) -> str:
@@ -124,7 +132,7 @@ async def _request_lnurl(url: str) -> dict:
124132

125133
status = response.get("status")
126134
if status and status == "ERROR":
127-
raise LNURLError(f"LNURL request encountered an error: {response.get('reason', '<missing reason>')}")
135+
raise UntrustedLNURLError(f"LNURL request encountered an error: {response.get('reason', '<missing reason>')}")
128136
return response
129137

130138

@@ -168,7 +176,7 @@ def _parse_lnurl3_response(lnurl_response: dict) -> LNURL3Data:
168176
"""Parses the server response received when requesting a LNURL-withdraw (lud3) request"""
169177
callback_url = _parse_lnurl_response_callback_url(lnurl_response)
170178
if not (k1 := lnurl_response.get('k1')):
171-
raise LNURLError(f"Missing k1 value in LNURL3 response: {lnurl_response=}")
179+
raise UntrustedLNURLError(f"Missing k1 value in LNURL3 response: {lnurl_response=}")
172180
default_description = lnurl_response.get('defaultDescription', '')
173181
try:
174182
min_withdrawable_sat = int(lnurl_response['minWithdrawable']) // 1000
@@ -194,7 +202,7 @@ async def request_lnurl(url: str) -> LNURLData:
194202
return _parse_lnurl6_response(lnurl_dict)
195203
elif tag == 'withdrawRequest':
196204
return _parse_lnurl3_response(lnurl_dict)
197-
raise LNURLError(f"Unknown subtype of lnurl. tag={tag}")
205+
raise UntrustedLNURLError(f"Unknown subtype of lnurl. tag={tag}")
198206

199207

200208
async def try_resolve_lnurlpay(lnurl: Optional[str]) -> Optional[LNURL6Data]:
@@ -236,7 +244,7 @@ async def callback_lnurl(url: str, params: dict) -> dict:
236244

237245
status = response.get("status")
238246
if status and status == "ERROR":
239-
raise LNURLError(f"LNURL request encountered an error: {response.get('reason', '<missing reason>')}")
247+
raise UntrustedLNURLError(f"LNURL request encountered an error: {response.get('reason', '<missing reason>')}")
240248
# TODO: handling of specific errors (validate fields, e.g. for lnurl6)
241249
return response
242250

0 commit comments

Comments
 (0)