// 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 #include "helpers/WebConfigKeys.h" // ---- allowlist ------------------------------------------------------------ TEST(WebConfigKeys, AllowsKnownScalarKeys) { EXPECT_TRUE(wcIsAllowedSetKey("name")); EXPECT_TRUE(wcIsAllowedSetKey("radio")); EXPECT_TRUE(wcIsAllowedSetKey("repeat")); EXPECT_TRUE(wcIsAllowedSetKey("wifi.ssid")); 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("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("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")); } // ---- 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("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; } } // ---- 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(); }