Skip to content

Commit c55f1ef

Browse files
authored
Merge pull request #782 from Crank-Git/validate-short-and-long-names
Add validation/prevent --set-owner, --set-owner-short, and --set-ham from accepting empty or whitespace-only names
2 parents ab997aa + 8a6ee5f commit c55f1ef

File tree

4 files changed

+209
-2
lines changed

4 files changed

+209
-2
lines changed

meshtastic/__main__.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,18 @@ def onConnected(interface):
342342
if args.set_owner or args.set_owner_short:
343343
closeNow = True
344344
waitForAckNak = True
345+
346+
# Validate owner names before connecting to device
347+
if args.set_owner is not None:
348+
stripped_long_name = args.set_owner.strip()
349+
if not stripped_long_name:
350+
meshtastic.util.our_exit("ERROR: Long Name cannot be empty or contain only whitespace characters")
351+
352+
if hasattr(args, 'set_owner_short') and args.set_owner_short is not None:
353+
stripped_short_name = args.set_owner_short.strip()
354+
if not stripped_short_name:
355+
meshtastic.util.our_exit("ERROR: Short Name cannot be empty or contain only whitespace characters")
356+
345357
if args.set_owner and args.set_owner_short:
346358
print(f"Setting device owner to {args.set_owner} and short name to {args.set_owner_short}")
347359
elif args.set_owner:
@@ -402,6 +414,8 @@ def onConnected(interface):
402414
print(" ".join(fieldNames))
403415

404416
if args.set_ham:
417+
if not args.set_ham.strip():
418+
meshtastic.util.our_exit("ERROR: Ham radio callsign cannot be empty or contain only whitespace characters")
405419
closeNow = True
406420
print(f"Setting Ham ID to {args.set_ham} and turning off encryption")
407421
interface.getNode(args.dest, **getNode_kwargs).setOwner(args.set_ham, is_licensed=True)
@@ -644,12 +658,20 @@ def onConnected(interface):
644658
interface.getNode(args.dest, False, **getNode_kwargs).beginSettingsTransaction()
645659

646660
if "owner" in configuration:
661+
# Validate owner name before setting
662+
owner_name = str(configuration["owner"]).strip()
663+
if not owner_name:
664+
meshtastic.util.our_exit("ERROR: Long Name cannot be empty or contain only whitespace characters")
647665
print(f"Setting device owner to {configuration['owner']}")
648666
waitForAckNak = True
649667
interface.getNode(args.dest, False, **getNode_kwargs).setOwner(configuration["owner"])
650668
time.sleep(0.5)
651669

652670
if "owner_short" in configuration:
671+
# Validate owner short name before setting
672+
owner_short_name = str(configuration["owner_short"]).strip()
673+
if not owner_short_name:
674+
meshtastic.util.our_exit("ERROR: Short Name cannot be empty or contain only whitespace characters")
653675
print(
654676
f"Setting device owner short to {configuration['owner_short']}"
655677
)
@@ -660,6 +682,10 @@ def onConnected(interface):
660682
time.sleep(0.5)
661683

662684
if "ownerShort" in configuration:
685+
# Validate owner short name before setting
686+
owner_short_name = str(configuration["ownerShort"]).strip()
687+
if not owner_short_name:
688+
meshtastic.util.our_exit("ERROR: Short Name cannot be empty or contain only whitespace characters")
663689
print(
664690
f"Setting device owner short to {configuration['ownerShort']}"
665691
)
@@ -1096,6 +1122,7 @@ def export_config(interface) -> str:
10961122
configObj["location"]["alt"] = alt
10971123

10981124
config = MessageToDict(interface.localNode.localConfig) #checkme - Used as a dictionary here and a string below
1125+
#was used as a string here and a Dictionary above
10991126
if config:
11001127
# Convert inner keys to correct snake/camelCase
11011128
prefs = {}
@@ -1190,6 +1217,22 @@ def common():
11901217
meshtastic.util.support_info()
11911218
meshtastic.util.our_exit("", 0)
11921219

1220+
# Early validation for owner names before attempting device connection
1221+
if hasattr(args, 'set_owner') and args.set_owner is not None:
1222+
stripped_long_name = args.set_owner.strip()
1223+
if not stripped_long_name:
1224+
meshtastic.util.our_exit("ERROR: Long Name cannot be empty or contain only whitespace characters")
1225+
1226+
if hasattr(args, 'set_owner_short') and args.set_owner_short is not None:
1227+
stripped_short_name = args.set_owner_short.strip()
1228+
if not stripped_short_name:
1229+
meshtastic.util.our_exit("ERROR: Short Name cannot be empty or contain only whitespace characters")
1230+
1231+
if hasattr(args, 'set_ham') and args.set_ham is not None:
1232+
stripped_ham_name = args.set_ham.strip()
1233+
if not stripped_ham_name:
1234+
meshtastic.util.our_exit("ERROR: Ham radio callsign cannot be empty or contain only whitespace characters")
1235+
11931236
if have_powermon:
11941237
create_power_meter()
11951238

meshtastic/node.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,10 +307,16 @@ def setOwner(self, long_name: Optional[str]=None, short_name: Optional[str]=None
307307
nChars = 4
308308
if long_name is not None:
309309
long_name = long_name.strip()
310+
# Validate that long_name is not empty or whitespace-only
311+
if not long_name:
312+
our_exit("ERROR: Long Name cannot be empty or contain only whitespace characters")
310313
p.set_owner.long_name = long_name
311314
p.set_owner.is_licensed = is_licensed
312315
if short_name is not None:
313316
short_name = short_name.strip()
317+
# Validate that short_name is not empty or whitespace-only
318+
if not short_name:
319+
our_exit("ERROR: Short Name cannot be empty or contain only whitespace characters")
314320
if len(short_name) > nChars:
315321
short_name = short_name[:nChars]
316322
print(f"Maximum is 4 characters, truncated to {short_name}")

meshtastic/tests/test_main.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2713,3 +2713,91 @@ def test_remove_ignored_node():
27132713
main()
27142714

27152715
mocked_node.removeIgnored.assert_called_once_with("!12345678")
2716+
@pytest.mark.unit
2717+
@pytest.mark.usefixtures("reset_mt_config")
2718+
def test_main_set_owner_whitespace_only(capsys):
2719+
"""Test --set-owner with whitespace-only name"""
2720+
sys.argv = ["", "--set-owner", " "]
2721+
mt_config.args = sys.argv
2722+
2723+
with pytest.raises(SystemExit) as excinfo:
2724+
main()
2725+
2726+
out, _ = capsys.readouterr()
2727+
assert "ERROR: Long Name cannot be empty or contain only whitespace characters" in out
2728+
assert excinfo.value.code == 1
2729+
2730+
2731+
@pytest.mark.unit
2732+
@pytest.mark.usefixtures("reset_mt_config")
2733+
def test_main_set_owner_empty_string(capsys):
2734+
"""Test --set-owner with empty string"""
2735+
sys.argv = ["", "--set-owner", ""]
2736+
mt_config.args = sys.argv
2737+
2738+
with pytest.raises(SystemExit) as excinfo:
2739+
main()
2740+
2741+
out, _ = capsys.readouterr()
2742+
assert "ERROR: Long Name cannot be empty or contain only whitespace characters" in out
2743+
assert excinfo.value.code == 1
2744+
2745+
2746+
@pytest.mark.unit
2747+
@pytest.mark.usefixtures("reset_mt_config")
2748+
def test_main_set_owner_short_whitespace_only(capsys):
2749+
"""Test --set-owner-short with whitespace-only name"""
2750+
sys.argv = ["", "--set-owner-short", " "]
2751+
mt_config.args = sys.argv
2752+
2753+
with pytest.raises(SystemExit) as excinfo:
2754+
main()
2755+
2756+
out, _ = capsys.readouterr()
2757+
assert "ERROR: Short Name cannot be empty or contain only whitespace characters" in out
2758+
assert excinfo.value.code == 1
2759+
2760+
2761+
@pytest.mark.unit
2762+
@pytest.mark.usefixtures("reset_mt_config")
2763+
def test_main_set_owner_short_empty_string(capsys):
2764+
"""Test --set-owner-short with empty string"""
2765+
sys.argv = ["", "--set-owner-short", ""]
2766+
mt_config.args = sys.argv
2767+
2768+
with pytest.raises(SystemExit) as excinfo:
2769+
main()
2770+
2771+
out, _ = capsys.readouterr()
2772+
assert "ERROR: Short Name cannot be empty or contain only whitespace characters" in out
2773+
assert excinfo.value.code == 1
2774+
2775+
2776+
@pytest.mark.unit
2777+
@pytest.mark.usefixtures("reset_mt_config")
2778+
def test_main_set_ham_whitespace_only(capsys):
2779+
"""Test --set-ham with whitespace-only name"""
2780+
sys.argv = ["", "--set-ham", " "]
2781+
mt_config.args = sys.argv
2782+
2783+
with pytest.raises(SystemExit) as excinfo:
2784+
main()
2785+
2786+
out, _ = capsys.readouterr()
2787+
assert "ERROR: Ham radio callsign cannot be empty or contain only whitespace characters" in out
2788+
assert excinfo.value.code == 1
2789+
2790+
2791+
@pytest.mark.unit
2792+
@pytest.mark.usefixtures("reset_mt_config")
2793+
def test_main_set_ham_empty_string(capsys):
2794+
"""Test --set-ham with empty string"""
2795+
sys.argv = ["", "--set-ham", ""]
2796+
mt_config.args = sys.argv
2797+
2798+
with pytest.raises(SystemExit) as excinfo:
2799+
main()
2800+
2801+
out, _ = capsys.readouterr()
2802+
assert "ERROR: Ham radio callsign cannot be empty or contain only whitespace characters" in out
2803+
assert excinfo.value.code == 1

meshtastic/tests/test_node.py

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,8 +1254,7 @@ def test_requestChannels_non_localNode_starting_index(caplog):
12541254
# },
12551255
# 'id': 1692918436,
12561256
# 'hopLimit': 3,
1257-
# 'priority':
1258-
# 'RELIABLE',
1257+
# 'priority': 'RELIABLE',
12591258
# 'raw': 'fake',
12601259
# 'fromId': '!9388f81c',
12611260
# 'toId': '!9388f81c'
@@ -1480,6 +1479,77 @@ def test_remove_ignored(ignored):
14801479
iface.sendData.assert_called_once()
14811480

14821481

1482+
@pytest.mark.unit
1483+
def test_setOwner_whitespace_only_long_name(capsys):
1484+
"""Test setOwner with whitespace-only long name"""
1485+
iface = MagicMock(autospec=MeshInterface)
1486+
anode = Node(iface, 123, noProto=True)
1487+
1488+
with pytest.raises(SystemExit) as excinfo:
1489+
anode.setOwner(long_name=" ")
1490+
1491+
out, _ = capsys.readouterr()
1492+
assert "ERROR: Long Name cannot be empty or contain only whitespace characters" in out
1493+
assert excinfo.value.code == 1
1494+
1495+
1496+
@pytest.mark.unit
1497+
def test_setOwner_empty_long_name(capsys):
1498+
"""Test setOwner with empty long name"""
1499+
iface = MagicMock(autospec=MeshInterface)
1500+
anode = Node(iface, 123, noProto=True)
1501+
1502+
with pytest.raises(SystemExit) as excinfo:
1503+
anode.setOwner(long_name="")
1504+
1505+
out, _ = capsys.readouterr()
1506+
assert "ERROR: Long Name cannot be empty or contain only whitespace characters" in out
1507+
assert excinfo.value.code == 1
1508+
1509+
1510+
@pytest.mark.unit
1511+
def test_setOwner_whitespace_only_short_name(capsys):
1512+
"""Test setOwner with whitespace-only short name"""
1513+
iface = MagicMock(autospec=MeshInterface)
1514+
anode = Node(iface, 123, noProto=True)
1515+
1516+
with pytest.raises(SystemExit) as excinfo:
1517+
anode.setOwner(short_name=" ")
1518+
1519+
out, _ = capsys.readouterr()
1520+
assert "ERROR: Short Name cannot be empty or contain only whitespace characters" in out
1521+
assert excinfo.value.code == 1
1522+
1523+
1524+
@pytest.mark.unit
1525+
def test_setOwner_empty_short_name(capsys):
1526+
"""Test setOwner with empty short name"""
1527+
iface = MagicMock(autospec=MeshInterface)
1528+
anode = Node(iface, 123, noProto=True)
1529+
1530+
with pytest.raises(SystemExit) as excinfo:
1531+
anode.setOwner(short_name="")
1532+
1533+
out, _ = capsys.readouterr()
1534+
assert "ERROR: Short Name cannot be empty or contain only whitespace characters" in out
1535+
assert excinfo.value.code == 1
1536+
1537+
1538+
@pytest.mark.unit
1539+
def test_setOwner_valid_names(caplog):
1540+
"""Test setOwner with valid names"""
1541+
iface = MagicMock(autospec=MeshInterface)
1542+
anode = Node(iface, 123, noProto=True)
1543+
1544+
with caplog.at_level(logging.DEBUG):
1545+
anode.setOwner(long_name="ValidName", short_name="VN")
1546+
1547+
# Should not raise any exceptions
1548+
# Note: When noProto=True, _sendAdmin is not called as the method returns early
1549+
assert re.search(r'p.set_owner.long_name:ValidName:', caplog.text, re.MULTILINE)
1550+
assert re.search(r'p.set_owner.short_name:VN:', caplog.text, re.MULTILINE)
1551+
1552+
14831553
# TODO
14841554
# @pytest.mark.unitslow
14851555
# def test_waitForConfig():

0 commit comments

Comments
 (0)