1
0

validate and deduplicate relay config input in qt gui

Adds validation and deduplication of the relay urls entered in the QT
settings dialog. This is supposed to prevent malformed or duplicated
relay entries.
Also resets the relays to the default value if no (valid) url
is entered. This prevents the user from getting stuck without relays
(otherwise the user would have to research for relay urls manually if
they don't know any).
This commit is contained in:
f321x
2025-04-30 14:15:01 +02:00
parent 4a8590077e
commit ee7d2ee17d
2 changed files with 34 additions and 5 deletions

View File

@@ -24,7 +24,7 @@
# SOFTWARE.
import ast
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Dict
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import (QComboBox, QTabWidget, QDialog, QSpinBox, QCheckBox, QLabel,
@@ -32,7 +32,7 @@ from PyQt6.QtWidgets import (QComboBox, QTabWidget, QDialog, QSpinBox, QCheckB
from electrum.i18n import _, languages
from electrum import util
from electrum.util import base_units_list, event_listener
from electrum.util import base_units_list, event_listener, is_valid_websocket_url
from electrum.gui import messages
@@ -181,7 +181,16 @@ class SettingsDialog(QDialog, QtEventListener):
self.nostr_relays_e = QLineEdit(nostr_relays)
def on_nostr_edit():
self.config.NOSTR_RELAYS = str(self.nostr_relays_e.text())
relays: Dict[str, None] = dict() # dicts keep insertion order
for url in self.nostr_relays_e.text().split(','):
url = url.strip()
if url and is_valid_websocket_url(url):
relays[url] = None
if relays.keys():
self.config.NOSTR_RELAYS = ",".join(relays.keys())
else: # if no valid relays are given, assign default relays from config
self.config.NOSTR_RELAYS = None
self.nostr_relays_e.setText(self.config.NOSTR_RELAYS)
self.nostr_relays_e.editingFinished.connect(on_nostr_edit)
msat_cb = checkbox_from_configvar(self.config.cv.BTC_AMOUNTS_PREC_POST_SAT)

View File

@@ -31,14 +31,13 @@ from typing import (NamedTuple, Union, TYPE_CHECKING, Tuple, Optional, Callable,
from datetime import datetime, timezone, timedelta
import decimal
from decimal import Decimal
import urllib
from urllib.parse import urlparse
import threading
import hmac
import hashlib
import stat
import locale
import asyncio
import urllib.request, urllib.parse, urllib.error
import builtins
import json
import time
@@ -699,6 +698,27 @@ def is_valid_email(s):
regexp = r"[^@]+@[^@]+\.[^@]+"
return re.match(regexp, s) is not None
def is_valid_websocket_url(url: str) -> bool:
"""
uses this django url validation regex:
https://github.com/django/django/blob/2c6906a0c4673a7685817156576724aba13ad893/django/core/validators.py#L45C1-L52C43
Note: this is not perfect, urls and their parsing can get very complex (see recent django code).
however its sufficient for catching weird user input in the gui dialog
"""
# stores the compiled regex in the function object itself to avoid recompiling it every call
if not hasattr(is_valid_websocket_url, "regex"):
is_valid_websocket_url.regex = re.compile(
r'^(?:ws|wss)://' # ws:// or wss://
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' # domain...
r'localhost|' # localhost...
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|' # ...or ipv4
r'\[?[A-F0-9]*:[A-F0-9:]+\]?)' # ...or ipv6
r'(?::\d+)?' # optional port
r'(?:/?|[/?]\S+)$', re.IGNORECASE)
try:
return re.match(is_valid_websocket_url.regex, url) is not None
except Exception:
return False
def is_hash256_str(text: Any) -> bool:
if not isinstance(text, str): return False