network: fix get_servers should not modify ports of DEFAULT_SERVERS
What changed, and why it matters
This commit fixes a bug where Electrum's built-in list of default servers could be accidentally modified by normal code. The fix makes a deep copy of the server list before returning it, so callers cannot change the original. This is a defensive coding fix that prevents potential network misdirection or connection problems, but the commit itself does not describe a specific security vulnerability or active attack.
Treat as a hardening fix. Review all callers of `get_servers()` to confirm none relied on mutating the returned object, and verify that deep-copying does not introduce performance issues. No immediate emergency response is warranted absent evidence of exploitation.
Security signals we found
Mutable global default server list could be altered by callers
Potential for server port or host tampering in memory
Defensive immutability fix for trusted network defaults
No explicit security advisory or CVE referenced in commit
Evidence from the diff
The AbstractNet.get_servers() classproperty in electrum/constants.py previously returned a direct reference to the cached DEFAULT_SERVERS dictionary loaded from servers.json. Any caller that mutated the returned structure (for example, changing server ports) would silently modify the global cached default. The patch imports copy and returns copy.deepcopy(d) instead, ensuring callers receive an independent copy and the canonical defaults remain immutable as intended.
Changed components
electrum/constants.pyAbstractNet.get_servers()DEFAULT_SERVERS / servers.json cacheInspect captured patch +3 / −1
diff --git a/electrum/constants.py b/electrum/constants.py
index b4b88d1..6026b9c 100644
--- a/electrum/constants.py
+++ b/electrum/constants.py
@@ -23,6 +23,7 @@
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
+import copy
import os
import json
from typing import Sequence, Tuple, Mapping, Type, List, Optional
@@ -100,7 +101,8 @@ class AbstractNet:
if cls._cached_default_servers is None:
default_file = {} if cls.TESTNET else None # for mainnet we hard-fail if the file is missing.
cls._cached_default_servers = read_json(os.path.join('chains', cls.NET_NAME, 'servers.json'), default_file)
- return cls._cached_default_servers
+ d = cls._cached_default_servers
+ return copy.deepcopy(d)
_cached_fallback_lnnodes = None
@classproperty
Why this scored 35/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.