#include #include #include "dd3_legacy_core.h" #include "html_util.h" static void test_html_escape_basic() { TEST_ASSERT_EQUAL_STRING("", html_escape("").c_str()); TEST_ASSERT_EQUAL_STRING("plain", html_escape("plain").c_str()); TEST_ASSERT_EQUAL_STRING("a&b", html_escape("a&b").c_str()); TEST_ASSERT_EQUAL_STRING("<tag>", html_escape("").c_str()); TEST_ASSERT_EQUAL_STRING(""hi"", html_escape("\"hi\"").c_str()); TEST_ASSERT_EQUAL_STRING("it's", html_escape("it's").c_str()); TEST_ASSERT_EQUAL_STRING("&<>"'", html_escape("&<>\"'").c_str()); } static void test_html_escape_adversarial() { TEST_ASSERT_EQUAL_STRING("&amp;", html_escape("&").c_str()); TEST_ASSERT_EQUAL_STRING("\n\r\t", html_escape("\n\r\t").c_str()); const String chunk = "<&>\"'abc\n\r\t"; const String escaped_chunk = "<&>"'abc\n\r\t"; const size_t repeats = 300; // 3.3 KB input String input; String expected; input.reserve(chunk.length() * repeats); expected.reserve(escaped_chunk.length() * repeats); for (size_t i = 0; i < repeats; ++i) { input += chunk; expected += escaped_chunk; } String out = html_escape(input); TEST_ASSERT_EQUAL_UINT(expected.length(), out.length()); TEST_ASSERT_EQUAL_STRING(expected.c_str(), out.c_str()); TEST_ASSERT_TRUE(out.indexOf("<&>"'abc") >= 0); } static void test_url_encode_component_table() { struct Case { const char *input; const char *expected; }; const Case cases[] = { {"", ""}, {"abcABC012-_.~", "abcABC012-_.~"}, {"a b", "a%20b"}, {"/\\?&#%\"'", "%2F%5C%3F%26%23%25%22%27"}, {"line\nbreak", "line%0Abreak"}, }; for (size_t i = 0; i < (sizeof(cases) / sizeof(cases[0])); ++i) { String out = url_encode_component(cases[i].input); TEST_ASSERT_EQUAL_STRING(cases[i].expected, out.c_str()); } String control; control += static_cast(0x01); control += static_cast(0x1F); control += static_cast(0x7F); TEST_ASSERT_EQUAL_STRING("%01%1F%7F", url_encode_component(control).c_str()); const String long_chunk = "AZaz09-_.~ /%?"; const String long_expected_chunk = "AZaz09-_.~%20%2F%25%3F"; String long_input; String long_expected; for (size_t i = 0; i < 40; ++i) { // 520 chars long_input += long_chunk; long_expected += long_expected_chunk; } String long_out_1 = url_encode_component(long_input); String long_out_2 = url_encode_component(long_input); TEST_ASSERT_EQUAL_STRING(long_expected.c_str(), long_out_1.c_str()); TEST_ASSERT_EQUAL_STRING(long_out_1.c_str(), long_out_2.c_str()); } static void test_sanitize_device_id_accepts_and_normalizes() { String out; const char *accept_cases[] = { "F19C", "f19c", " f19c ", "dd3-f19c", "dd3-F19C", "dd3-a0b1", }; for (size_t i = 0; i < (sizeof(accept_cases) / sizeof(accept_cases[0])); ++i) { TEST_ASSERT_TRUE(sanitize_device_id(accept_cases[i], out)); if (String(accept_cases[i]).indexOf("a0b1") >= 0) { TEST_ASSERT_EQUAL_STRING("dd3-A0B1", out.c_str()); } else { TEST_ASSERT_EQUAL_STRING("dd3-F19C", out.c_str()); } } } static void test_sanitize_device_id_rejects_invalid() { String out = "dd3-KEEP"; const char *reject_cases[] = { "", "F", "FFF", "FFFFF", "dd3-12", "dd3-12345", "F1 9C", "dd3-F1\t9C", "dd3-F19C%00", "%F19C", "../F19C", "dd3-..1A", "dd3-12/3", "dd3-12\\3", "F19G", "dd3-zzzz", }; for (size_t i = 0; i < (sizeof(reject_cases) / sizeof(reject_cases[0])); ++i) { TEST_ASSERT_FALSE(sanitize_device_id(reject_cases[i], out)); } TEST_ASSERT_EQUAL_STRING("dd3-KEEP", out.c_str()); } void setup() { dd3_legacy_core_force_link(); UNITY_BEGIN(); RUN_TEST(test_html_escape_basic); RUN_TEST(test_html_escape_adversarial); RUN_TEST(test_url_encode_component_table); RUN_TEST(test_sanitize_device_id_accepts_and_normalizes); RUN_TEST(test_sanitize_device_id_rejects_invalid); UNITY_END(); } void loop() {}