What changed, and why it matters
This commit fixes a small but real bug in Electrum's handling of private Lightning-style 'onion' messages. When a message uses a hidden route with only one intermediate hop, the software could receive that hop as a single object instead of a list, causing later code to crash or behave unexpectedly. The patch forces it to always be treated as a list. The most likely effect without the patch is a local error or failed message delivery, but in the worst case it could be abused to make the wallet mishandle a message.
Review the full blinded_path parsing and serialization code to ensure list consistency at the source, add type validation and tests for single-hop paths, and consider fuzzing or unit tests for malformed onion_message payloads.
Security signals we found
Type confusion / non-list iterable handling in protocol message path parsing
Defensive normalization of externally supplied structured data
Crash or logic-error potential when blinded path length equals one
Partial fix: root cause in upstream data format not addressed here
Evidence from the diff
In electrum/onion_message.py, send_onion_message_to() now normalizes blinded_path[‘path’] to a list. The comment indicates the upstream source does not return a list when the path contains exactly one item. Without normalization, downstream iteration over remaining_blinded_path would operate on a single object (likely a dict), raising AttributeError/TypeError or producing incorrect per-hop processing. The patch is defensive and partial: it only coerces the value locally and does not fix the serialization/parser that produced the non-list value.
Changed components
electrum/onion_message.pysend_onion_message_to()Lightning Network onion/blinded-path message handlingInspect captured patch +3 / −0
diff --git a/electrum/onion_message.py b/electrum/onion_message.py
index 9c4e570..574fcbf 100644
--- a/electrum/onion_message.py
+++ b/electrum/onion_message.py
@@ -249,6 +249,9 @@ def send_onion_message_to(
else:
# we need a route to introduction point
remaining_blinded_path = blinded_path['path']
+ if not isinstance(remaining_blinded_path, list): # doesn't return list when num items == 1
+ remaining_blinded_path = [remaining_blinded_path]
+
peer = lnwallet.peers.get(introduction_point)
# if blinded path introduction point is our direct peer, no need to route-find
if peer:
Why this scored 47/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.