diff --git a/test/test_html_escape/test_html_escape.cpp b/test/test_html_escape/test_html_escape.cpp
index 2f782cc..bec1753 100644
--- a/test/test_html_escape/test_html_escape.cpp
+++ b/test/test_html_escape/test_html_escape.cpp
@@ -12,25 +12,121 @@ static void test_html_escape_basic() {
TEST_ASSERT_EQUAL_STRING("&<>"'", html_escape("&<>\"'").c_str());
}
-static void test_sanitize_device_id() {
+static void test_html_escape_adversarial() {
+ TEST_ASSERT_EQUAL_STRING("&", 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;
- TEST_ASSERT_TRUE(sanitize_device_id("F19C", out));
- TEST_ASSERT_EQUAL_STRING("dd3-F19C", out.c_str());
- TEST_ASSERT_TRUE(sanitize_device_id("dd3-f19c", out));
- TEST_ASSERT_EQUAL_STRING("dd3-F19C", out.c_str());
- TEST_ASSERT_FALSE(sanitize_device_id("F19G", out));
- TEST_ASSERT_FALSE(sanitize_device_id("dd3-12", out));
- TEST_ASSERT_FALSE(sanitize_device_id("dd3-12345", out));
- TEST_ASSERT_FALSE(sanitize_device_id("../F19C", out));
- TEST_ASSERT_FALSE(sanitize_device_id("dd3-%2f", out));
- TEST_ASSERT_FALSE(sanitize_device_id("dd3-12/3", out));
- TEST_ASSERT_FALSE(sanitize_device_id("dd3-12\\3", 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() {
UNITY_BEGIN();
RUN_TEST(test_html_escape_basic);
- RUN_TEST(test_sanitize_device_id);
+ 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();
}