Files
HaloKeymind/test/test_webconfig_keys/test_webconfig_keys.cpp
T
mikecarper 6958d82432 Add independent Companion Bluetooth stealth flag
Keep stealth separate from custom, saved-random and rotating MAC policies without migrating the development MAC mode. Persist authenticated peer state, guard advertising transitions and recovery, and expose the setting through the CLI and WebConfig.

Add regression and CI checks and record exact XIAO BLE pairing, reconnect, reboot and address-rotation hardware results.
2026-09-07 23:46:55 -07:00

234 lines
9.4 KiB
C++

// Host tests for the WebConfig key allowlist / secret / slot-prefix helpers
// (src/helpers/WebConfigKeys.h). These parse attacker-supplied POST keys, so
// coverage of the length-guard and boundary cases matters for safety.
#include <gtest/gtest.h>
#include "helpers/WebConfigKeys.h"
// ---- allowlist ------------------------------------------------------------
TEST(WebConfigKeys, AllowsKnownScalarKeys) {
EXPECT_TRUE(wcIsAllowedSetKey("name"));
EXPECT_TRUE(wcIsAllowedSetKey("bluetooth.name"));
EXPECT_TRUE(wcIsAllowedSetKey("bluetooth.mac"));
EXPECT_TRUE(wcIsAllowedSetKey("bluetooth.stealth"));
EXPECT_TRUE(wcIsAllowedSetKey("radio"));
EXPECT_TRUE(wcIsAllowedSetKey("radio.rxps"));
EXPECT_TRUE(wcIsAllowedSetKey("powersaving"));
EXPECT_TRUE(wcIsAllowedSetKey("repeat"));
EXPECT_TRUE(wcIsAllowedSetKey("wifi.ssid"));
EXPECT_TRUE(wcIsAllowedSetKey("espnow.channel"));
EXPECT_TRUE(wcIsAllowedSetKey("mqtt.iata"));
EXPECT_TRUE(wcIsAllowedSetKey("mqtt.neighbors"));
EXPECT_TRUE(wcIsAllowedSetKey("mqtt.neighbors.interval"));
EXPECT_TRUE(wcIsAllowedSetKey("snmp.community"));
EXPECT_TRUE(wcIsAllowedSetKey("timezone.offset"));
}
TEST(WebConfigKeys, AllowsPerSlotKeys) {
EXPECT_TRUE(wcIsAllowedSetKey("mqtt1.preset"));
EXPECT_TRUE(wcIsAllowedSetKey("mqtt1.server"));
EXPECT_TRUE(wcIsAllowedSetKey("mqtt1.token"));
EXPECT_TRUE(wcIsAllowedSetKey("mqtt2.filter"));
EXPECT_TRUE(wcIsAllowedSetKey("mqtt6.audience")); // MAX_MQTT_SLOTS == 6
}
TEST(WebConfigKeys, RejectsDangerousOrUnknownKeys) {
EXPECT_FALSE(wcIsAllowedSetKey("erase"));
EXPECT_FALSE(wcIsAllowedSetKey("password"));
EXPECT_FALSE(wcIsAllowedSetKey("reboot"));
EXPECT_FALSE(wcIsAllowedSetKey("bogus"));
EXPECT_FALSE(wcIsAllowedSetKey("bluetooth.stealth.extra"));
EXPECT_FALSE(wcIsAllowedSetKey("mqtt1.bogus")); // unknown slot field
EXPECT_FALSE(wcIsAllowedSetKey(""));
}
TEST(WebConfigKeys, AdminPasswordIsNotAnAllowlistedSetKey) {
EXPECT_TRUE(wcIsAdminPasswordKey("password"));
EXPECT_FALSE(wcIsAdminPasswordKey("admin.password"));
EXPECT_FALSE(wcIsAdminPasswordKey(""));
EXPECT_FALSE(wcIsAllowedSetKey("password")); // never reachable as `set password`
}
TEST(WebConfigKeys, AdminPasswordFitsNodePrefsAndRejectsLineBreaks) {
EXPECT_FALSE(wcIsValidAdminPassword(NULL));
EXPECT_FALSE(wcIsValidAdminPassword(""));
EXPECT_TRUE(wcIsValidAdminPassword("new-password"));
EXPECT_TRUE(wcIsValidAdminPassword("123456789012345"));
EXPECT_FALSE(wcIsValidAdminPassword("1234567890123456"));
EXPECT_FALSE(wcIsValidAdminPassword("line\nbreak"));
EXPECT_FALSE(wcIsValidAdminPassword("line\rbreak"));
}
TEST(WebConfigKeys, SlotIndexBoundsMatchMaxSlots) {
EXPECT_FALSE(wcIsAllowedSetKey("mqtt0.preset")); // slot 0 invalid
EXPECT_TRUE(wcIsAllowedSetKey("mqtt6.preset")); // last valid slot
EXPECT_FALSE(wcIsAllowedSetKey("mqtt7.preset")); // beyond MAX_MQTT_SLOTS
EXPECT_FALSE(wcIsAllowedSetKey("mqtt9.preset"));
}
TEST(WebConfigKeys, IsCaseSensitive) {
EXPECT_FALSE(wcIsAllowedSetKey("Name"));
EXPECT_FALSE(wcIsAllowedSetKey("MQTT1.preset"));
}
TEST(WebConfigKeys, AddressChangingKeysAlwaysRequireAReboot) {
EXPECT_TRUE(wcSetKeyRequiresReboot("espnow.channel"));
EXPECT_TRUE(wcSetKeyRequiresReboot("bluetooth.mac"));
EXPECT_TRUE(wcSetKeyRequiresReboot("bluetooth.stealth"));
EXPECT_FALSE(wcSetKeyRequiresReboot("bluetooth.stealth.extra"));
EXPECT_FALSE(wcSetKeyRequiresReboot(NULL));
EXPECT_FALSE(wcSetKeyRequiresReboot("espnow.channel.extra"));
EXPECT_FALSE(wcSetKeyRequiresReboot("ESPNOW.channel"));
EXPECT_FALSE(wcSetKeyRequiresReboot("wifi.ssid"));
EXPECT_FALSE(wcSetKeyRequiresReboot(""));
}
TEST(WebConfigKeys, EspNowChannelClassificationIsNarrow) {
EXPECT_TRUE(wcIsEspNowChannelKey("espnow.channel"));
EXPECT_FALSE(wcIsEspNowChannelKey("bluetooth.mac"));
EXPECT_FALSE(wcIsEspNowChannelKey("espnow.channel.extra"));
EXPECT_FALSE(wcIsEspNowChannelKey(NULL));
}
// ---- short-key OOB guard --------------------------------------------------
// The slot-prefix probe indexes key[4..6]; these short strings must be rejected
// without ever reading past the terminator.
TEST(WebConfigKeys, ShortKeysRejectedSafely) {
EXPECT_FALSE(wcIsSlotKeyPrefix(""));
EXPECT_FALSE(wcIsSlotKeyPrefix("m"));
EXPECT_FALSE(wcIsSlotKeyPrefix("mq"));
EXPECT_FALSE(wcIsSlotKeyPrefix("mqt"));
EXPECT_FALSE(wcIsSlotKeyPrefix("mqtt")); // 4 chars -- no digit/dot
EXPECT_FALSE(wcIsSlotKeyPrefix("mqtt1")); // 5 chars -- no dot
EXPECT_FALSE(wcIsSlotKeyPrefix("mqtt1.")); // 6 chars -- no field char
EXPECT_TRUE(wcIsSlotKeyPrefix("mqtt1.x")); // 7 chars -- minimum valid
// Same guard via the public allowlist/secret entry points:
EXPECT_FALSE(wcIsAllowedSetKey("mqtt"));
EXPECT_FALSE(wcIsSecretKey("m"));
EXPECT_FALSE(wcIsSecretKey("mqtt"));
}
TEST(WebConfigKeys, SlotPrefixDigitRange) {
EXPECT_FALSE(wcIsSlotKeyPrefix("mqtt0.x"));
EXPECT_TRUE(wcIsSlotKeyPrefix("mqtt6.x"));
EXPECT_FALSE(wcIsSlotKeyPrefix("mqtt7.x"));
EXPECT_FALSE(wcIsSlotKeyPrefix("mqttA.x")); // non-digit
}
// ---- secret classification ------------------------------------------------
TEST(WebConfigKeys, SecretKeysDetected) {
EXPECT_TRUE(wcIsSecretKey("wifi.pwd"));
EXPECT_TRUE(wcIsSecretKey("mqtt1.password"));
EXPECT_TRUE(wcIsSecretKey("mqtt3.token"));
EXPECT_TRUE(wcIsSecretKey("mqtt6.password"));
}
TEST(WebConfigKeys, NonSecretKeysNotFlagged) {
EXPECT_FALSE(wcIsSecretKey("wifi.ssid"));
EXPECT_FALSE(wcIsSecretKey("mqtt1.username")); // username is not masked
EXPECT_FALSE(wcIsSecretKey("mqtt1.server"));
EXPECT_FALSE(wcIsSecretKey("mqtt1.filter"));
EXPECT_FALSE(wcIsSecretKey("mqtt.origin"));
EXPECT_FALSE(wcIsSecretKey("name"));
EXPECT_FALSE(wcIsSecretKey(""));
}
TEST(WebConfigKeys, EverySecretKeyIsAlsoAllowed) {
// A secret key must be one the portal can actually set, or the masking is moot.
const char* secrets[] = {"wifi.pwd", "mqtt1.password", "mqtt1.token",
"mqtt6.password", "mqtt6.token"};
for (const char* k : secrets) {
EXPECT_TRUE(wcIsSecretKey(k)) << k;
EXPECT_TRUE(wcIsAllowedSetKey(k)) << k;
}
}
// ---- CLI secret reads ----------------------------------------------------
// The web CLI runs commands with sender_timestamp 0, which is how CommonCLI
// recognises the serial console and answers secret getters in plaintext. These
// are the reads that must be masked back down for an HTTP caller.
TEST(WebConfigKeys, MasksEverySecretReadTheCliCanReach) {
const char* masked[] = {
"get prv.key", // this node's identity -- the worst one to leak
"get wifi.pwd", // grants the operator's LAN, not just the node
"get guest.password",
"get alert.psk",
"get bridge.secret",
"get mqtt1.password", "get mqtt1.token",
"get mqtt6.password", "get mqtt6.token",
"get wifi.pwd", // extra space after the verb
};
for (const char* c : masked) EXPECT_TRUE(wcIsSecretReadCommand(c)) << c;
}
TEST(WebConfigKeys, DoesNotMaskReadsThatCarryNoSecret) {
const char* plain[] = {
"get wifi.ssid", "get mqtt1.username", "get mqtt1.server", "get tx",
"get public.key", // public half, safe to read
"get mqtt.owner", // an owner's public key, not a credential
};
for (const char* c : plain) EXPECT_FALSE(wcIsSecretReadCommand(c)) << c;
}
TEST(WebConfigKeys, OnlyMasksReads) {
// Writing a secret has always been the portal's job and reveals nothing;
// only the read is restricted. Nor may a prefix be mistaken for a `get`.
EXPECT_FALSE(wcIsSecretReadCommand("set wifi.pwd hunter2"));
EXPECT_FALSE(wcIsSecretReadCommand("set prv.key aabb"));
EXPECT_FALSE(wcIsSecretReadCommand("password hunter2"));
EXPECT_FALSE(wcIsSecretReadCommand("getwifi.pwd"));
EXPECT_FALSE(wcIsSecretReadCommand("get"));
EXPECT_FALSE(wcIsSecretReadCommand(""));
}
// ---- request correlation -------------------------------------------------
TEST(WebConfigKeys, AcceptsExactHexRequestIds) {
EXPECT_TRUE(wcIsValidReqId("0123456789abcdef"));
EXPECT_TRUE(wcIsValidReqId("ABCDEF0123456789"));
}
TEST(WebConfigKeys, RejectsMissingMalformedOrWrongLengthRequestIds) {
EXPECT_FALSE(wcIsValidReqId(NULL));
EXPECT_FALSE(wcIsValidReqId(""));
EXPECT_FALSE(wcIsValidReqId("0123456789abcde"));
EXPECT_FALSE(wcIsValidReqId("0123456789abcdef0"));
EXPECT_FALSE(wcIsValidReqId("0123456789abcdeg"));
EXPECT_FALSE(wcIsValidReqId("01234567-9abcdef"));
EXPECT_FALSE(wcIsValidReqId("01234567 9abcdef"));
}
// ---- browser terminal commands -------------------------------------------
TEST(WebConfigKeys, AcceptsSingleLineCliCommands) {
EXPECT_TRUE(wcIsValidCliCommand("get wifi.status"));
EXPECT_TRUE(wcIsValidCliCommand("set flood.max 12"));
EXPECT_TRUE(wcIsValidCliCommand(" neighbors "));
}
TEST(WebConfigKeys, RejectsBlankMultilineOrOversizedCliCommands) {
EXPECT_FALSE(wcIsValidCliCommand(NULL));
EXPECT_FALSE(wcIsValidCliCommand(""));
EXPECT_FALSE(wcIsValidCliCommand(" \t"));
EXPECT_FALSE(wcIsValidCliCommand("get role\nreboot"));
EXPECT_FALSE(wcIsValidCliCommand("get role\rreboot"));
char max_command[160];
memset(max_command, 'x', sizeof(max_command) - 1);
max_command[sizeof(max_command) - 1] = 0;
EXPECT_TRUE(wcIsValidCliCommand(max_command));
char oversized[161];
memset(oversized, 'x', sizeof(oversized) - 1);
oversized[sizeof(oversized) - 1] = 0;
EXPECT_FALSE(wcIsValidCliCommand(oversized));
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}