Files
HaloKeymind/test/test_webconfig_keys/test_webconfig_keys.cpp
T
agessaman dc44311e49 feat(webconfig): validate request IDs and improve error handling
Add validation for request IDs in the web configuration server to ensure
they conform to the expected format. Enhance error responses for invalid
or unknown request IDs, improving the robustness of request handling.
Update the web UI to reflect these changes, ensuring that clients can
properly handle errors related to request ID mismatches.
2026-07-18 14:25:56 -07:00

122 lines
4.6 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("radio"));
EXPECT_TRUE(wcIsAllowedSetKey("repeat"));
EXPECT_TRUE(wcIsAllowedSetKey("wifi.ssid"));
EXPECT_TRUE(wcIsAllowedSetKey("mqtt.iata"));
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, 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"));
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}