1
0

Merge pull request #9773 from f321x/improve_relay_string_input

qt: validate and deduplicate relay config input in qt gui
This commit is contained in:
accumulator
2025-05-02 10:36:22 +02:00
committed by GitHub
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