/* Tests in the "namespace" test case for the Expat test suite __ __ _ ___\ \/ /_ __ __ _| |_ / _ \\ /| '_ \ / _` | __| | __// \| |_) | (_| | |_ \___/_/\_\ .__/ \__,_|\__| |_| XML parser Copyright (c) 2001-2006 Fred L. Drake, Jr. Copyright (c) 2003 Greg Stein Copyright (c) 2005-2007 Steven Solie Copyright (c) 2005-2012 Karl Waclawek Copyright (c) 2016-2023 Sebastian Pipping Copyright (c) 2017-2022 Rhodri James Copyright (c) 2017 Joe Orton Copyright (c) 2017 José Gutiérrez de la Concha Copyright (c) 2018 Marco Maggi Copyright (c) 2019 David Loffredo Copyright (c) 2020 Tim Gates Copyright (c) 2021 Donghee Na Copyright (c) 2023 Sony Corporation / Snild Dolkow Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "expat_config.h" #include #include "expat.h" #include "internal.h" #include "minicheck.h" #include "common.h" #include "dummy.h" #include "handlers.h" #include "ns_tests.h" static void namespace_setup(void) { g_parser = XML_ParserCreateNS(NULL, XCS(' ')); if (g_parser == NULL) fail("Parser not created."); } static void namespace_teardown(void) { basic_teardown(); } START_TEST(test_return_ns_triplet) { const char *text = ""; const char *epilog = ""; const XML_Char *elemstr[] = {XCS("http://example.org/ e foo"), XCS("http://example.org/ a bar")}; XML_SetReturnNSTriplet(g_parser, XML_TRUE); XML_SetUserData(g_parser, (void *)elemstr); XML_SetElementHandler(g_parser, triplet_start_checker, triplet_end_checker); XML_SetNamespaceDeclHandler(g_parser, dummy_start_namespace_decl_handler, dummy_end_namespace_decl_handler); g_triplet_start_flag = XML_FALSE; g_triplet_end_flag = XML_FALSE; init_dummy_handlers(); if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_FALSE) == XML_STATUS_ERROR) xml_failure(g_parser); /* Check that unsetting "return triplets" fails while still parsing */ XML_SetReturnNSTriplet(g_parser, XML_FALSE); if (_XML_Parse_SINGLE_BYTES(g_parser, epilog, (int)strlen(epilog), XML_TRUE) == XML_STATUS_ERROR) xml_failure(g_parser); if (! g_triplet_start_flag) fail("triplet_start_checker not invoked"); if (! g_triplet_end_flag) fail("triplet_end_checker not invoked"); if (get_dummy_handler_flags() != (DUMMY_START_NS_DECL_HANDLER_FLAG | DUMMY_END_NS_DECL_HANDLER_FLAG)) fail("Namespace handlers not called"); } END_TEST /* Test that the parsing status is correctly reset by XML_ParserReset(). * We use test_return_ns_triplet() for our example parse to improve * coverage of tidying up code executed. */ START_TEST(test_ns_parser_reset) { XML_ParsingStatus status; XML_GetParsingStatus(g_parser, &status); if (status.parsing != XML_INITIALIZED) fail("parsing status doesn't start INITIALIZED"); test_return_ns_triplet(); XML_GetParsingStatus(g_parser, &status); if (status.parsing != XML_FINISHED) fail("parsing status doesn't end FINISHED"); XML_ParserReset(g_parser, NULL); XML_GetParsingStatus(g_parser, &status); if (status.parsing != XML_INITIALIZED) fail("parsing status doesn't reset to INITIALIZED"); } END_TEST static void run_ns_tagname_overwrite_test(const char *text, const XML_Char *result) { CharData storage; CharData_Init(&storage); XML_SetUserData(g_parser, &storage); XML_SetElementHandler(g_parser, overwrite_start_checker, overwrite_end_checker); if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(g_parser); CharData_CheckXMLChars(&storage, result); } /* Regression test for SF bug #566334. */ START_TEST(test_ns_tagname_overwrite) { const char *text = "\n" " \n" " \n" ""; const XML_Char *result = XCS("start http://example.org/ e\n") XCS("start http://example.org/ f\n") XCS("attribute http://example.org/ attr\n") XCS("end http://example.org/ f\n") XCS("start http://example.org/ g\n") XCS("attribute http://example.org/ attr2\n") XCS("end http://example.org/ g\n") XCS("end http://example.org/ e\n"); run_ns_tagname_overwrite_test(text, result); } END_TEST /* Regression test for SF bug #566334. */ START_TEST(test_ns_tagname_overwrite_triplet) { const char *text = "\n" " \n" " \n" ""; const XML_Char *result = XCS("start http://example.org/ e n\n") XCS("start http://example.org/ f n\n") XCS("attribute http://example.org/ attr n\n") XCS("end http://example.org/ f n\n") XCS("start http://example.org/ g n\n") XCS("attribute http://example.org/ attr2 n\n") XCS("end http://example.org/ g n\n") XCS("end http://example.org/ e n\n"); XML_SetReturnNSTriplet(g_parser, XML_TRUE); run_ns_tagname_overwrite_test(text, result); } END_TEST /* Regression test for SF bug #620343. */ START_TEST(test_start_ns_clears_start_element) { /* This needs to use separate start/end tags; using the empty tag syntax doesn't cause the problematic path through Expat to be taken. */ const char *text = ""; XML_SetStartElementHandler(g_parser, start_element_fail); XML_SetStartNamespaceDeclHandler(g_parser, start_ns_clearing_start_element); XML_SetEndNamespaceDeclHandler(g_parser, dummy_end_namespace_decl_handler); XML_UseParserAsHandlerArg(g_parser); if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(g_parser); } END_TEST /* Regression test for SF bug #616863. */ START_TEST(test_default_ns_from_ext_subset_and_ext_ge) { const char *text = "\n" "\n" "]>\n" "\n" "&en;\n" ""; XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(g_parser, external_entity_handler); /* We actually need to set this handler to tickle this bug. */ XML_SetStartElementHandler(g_parser, dummy_start_element); XML_SetUserData(g_parser, NULL); if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(g_parser); } END_TEST /* Regression test #1 for SF bug #673791. */ START_TEST(test_ns_prefix_with_empty_uri_1) { const char *text = "\n" " \n" ""; expect_failure(text, XML_ERROR_UNDECLARING_PREFIX, "Did not report re-setting namespace" " URI with prefix to ''."); } END_TEST /* Regression test #2 for SF bug #673791. */ START_TEST(test_ns_prefix_with_empty_uri_2) { const char *text = "\n" ""; expect_failure(text, XML_ERROR_UNDECLARING_PREFIX, "Did not report setting namespace URI with prefix to ''."); } END_TEST /* Regression test #3 for SF bug #673791. */ START_TEST(test_ns_prefix_with_empty_uri_3) { const char *text = "\n" " \n" "]>\n" ""; expect_failure(text, XML_ERROR_UNDECLARING_PREFIX, "Didn't report attr default setting NS w/ prefix to ''."); } END_TEST /* Regression test #4 for SF bug #673791. */ START_TEST(test_ns_prefix_with_empty_uri_4) { const char *text = "\n" " \n" "]>\n" ""; /* Packaged info expected by the end element handler; the weird structuring lets us reuse the triplet_end_checker() function also used for another test. */ const XML_Char *elemstr[] = {XCS("http://example.org/ doc prefix")}; XML_SetReturnNSTriplet(g_parser, XML_TRUE); XML_SetUserData(g_parser, (void *)elemstr); XML_SetEndElementHandler(g_parser, triplet_end_checker); if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(g_parser); } END_TEST /* Test with non-xmlns prefix */ START_TEST(test_ns_unbound_prefix) { const char *text = "\n" " \n" "]>\n" ""; if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) != XML_STATUS_ERROR) fail("Unbound prefix incorrectly passed"); if (XML_GetErrorCode(g_parser) != XML_ERROR_UNBOUND_PREFIX) xml_failure(g_parser); } END_TEST START_TEST(test_ns_default_with_empty_uri) { const char *text = "\n" " \n" ""; /* Add some handlers to exercise extra code paths */ XML_SetStartNamespaceDeclHandler(g_parser, dummy_start_namespace_decl_handler); XML_SetEndNamespaceDeclHandler(g_parser, dummy_end_namespace_decl_handler); if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(g_parser); } END_TEST /* Regression test for SF bug #692964: two prefixes for one namespace. */ START_TEST(test_ns_duplicate_attrs_diff_prefixes) { const char *text = ""; expect_failure(text, XML_ERROR_DUPLICATE_ATTRIBUTE, "did not report multiple attributes with same URI+name"); } END_TEST START_TEST(test_ns_duplicate_hashes) { /* The hash of an attribute is calculated as the hash of its URI * concatenated with a space followed by its name (after the * colon). We wish to generate attributes with the same hash * value modulo the attribute table size so that we can check that * the attribute hash table works correctly. The attribute hash * table size will be the smallest power of two greater than the * number of attributes, but at least eight. There is * unfortunately no programmatic way of getting the hash or the * table size at user level, but the test code coverage percentage * will drop if the hashes cease to point to the same row. * * The cunning plan is to have few enough attributes to have a * reliable table size of 8, and have the single letter attribute * names be 8 characters apart, producing a hash which will be the * same modulo 8. */ const char *text = ""; if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(g_parser); } END_TEST /* Regression test for SF bug #695401: unbound prefix. */ START_TEST(test_ns_unbound_prefix_on_attribute) { const char *text = ""; expect_failure(text, XML_ERROR_UNBOUND_PREFIX, "did not report unbound prefix on attribute"); } END_TEST /* Regression test for SF bug #695401: unbound prefix. */ START_TEST(test_ns_unbound_prefix_on_element) { const char *text = ""; expect_failure(text, XML_ERROR_UNBOUND_PREFIX, "did not report unbound prefix on element"); } END_TEST /* Test that long element names with namespaces are handled correctly */ START_TEST(test_ns_long_element) { const char *text = "" ""; const XML_Char *elemstr[] = {XCS("http://example.org/") XCS(" thisisalongenoughelementnametotriggerareallocation foo"), XCS("http://example.org/ a bar")}; XML_SetReturnNSTriplet(g_parser, XML_TRUE); XML_SetUserData(g_parser, (void *)elemstr); XML_SetElementHandler(g_parser, triplet_start_checker, triplet_end_checker); if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(g_parser); } END_TEST /* Test mixed population of prefixed and unprefixed attributes */ START_TEST(test_ns_mixed_prefix_atts) { const char *text = "" ""; if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(g_parser); } END_TEST /* Test having a long namespaced element name inside a short one. * This exercises some internal buffer reallocation that is shared * across elements with the same namespace URI. */ START_TEST(test_ns_extend_uri_buffer) { const char *text = "" " " ""; if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(g_parser); } END_TEST /* Test that xmlns is correctly rejected as an attribute in the xmlns * namespace, but not in other namespaces */ START_TEST(test_ns_reserved_attributes) { const char *text1 = ""; const char *text2 = ""; expect_failure(text1, XML_ERROR_RESERVED_PREFIX_XMLNS, "xmlns not rejected as an attribute"); XML_ParserReset(g_parser, NULL); if (_XML_Parse_SINGLE_BYTES(g_parser, text2, (int)strlen(text2), XML_TRUE) == XML_STATUS_ERROR) xml_failure(g_parser); } END_TEST /* Test more reserved attributes */ START_TEST(test_ns_reserved_attributes_2) { const char *text1 = ""; const char *text2 = ""; const char *text3 = ""; expect_failure(text1, XML_ERROR_RESERVED_PREFIX_XML, "xml not rejected as an attribute"); XML_ParserReset(g_parser, NULL); expect_failure(text2, XML_ERROR_RESERVED_NAMESPACE_URI, "Use of w3.org URL not faulted"); XML_ParserReset(g_parser, NULL); expect_failure(text3, XML_ERROR_RESERVED_NAMESPACE_URI, "Use of w3.org xmlns URL not faulted"); } END_TEST /* Test string pool handling of namespace names of 2048 characters */ /* Exercises a particular string pool growth path */ START_TEST(test_ns_extremely_long_prefix) { /* C99 compilers are only required to support 4095-character * strings, so the following needs to be split in two to be safe * for all compilers. */ const char *text1 = "" ""; if (_XML_Parse_SINGLE_BYTES(g_parser, text1, (int)strlen(text1), XML_FALSE) == XML_STATUS_ERROR) xml_failure(g_parser); if (_XML_Parse_SINGLE_BYTES(g_parser, text2, (int)strlen(text2), XML_TRUE) == XML_STATUS_ERROR) xml_failure(g_parser); } END_TEST /* Test unknown encoding handlers in namespace setup */ START_TEST(test_ns_unknown_encoding_success) { const char *text = "\n" "Hi"; XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL); run_character_check(text, XCS("Hi")); } END_TEST /* Test that too many colons are rejected */ START_TEST(test_ns_double_colon) { const char *text = ""; const enum XML_Status status = _XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE); #ifdef XML_NS if ((status == XML_STATUS_OK) || (XML_GetErrorCode(g_parser) != XML_ERROR_INVALID_TOKEN)) { fail("Double colon in attribute name not faulted" " (despite active namespace support)"); } #else if (status != XML_STATUS_OK) { fail("Double colon in attribute name faulted" " (despite inactive namespace support"); } #endif } END_TEST START_TEST(test_ns_double_colon_element) { const char *text = ""; const enum XML_Status status = _XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE); #ifdef XML_NS if ((status == XML_STATUS_OK) || (XML_GetErrorCode(g_parser) != XML_ERROR_INVALID_TOKEN)) { fail("Double colon in element name not faulted" " (despite active namespace support)"); } #else if (status != XML_STATUS_OK) { fail("Double colon in element name faulted" " (despite inactive namespace support"); } #endif } END_TEST /* Test that non-name characters after a colon are rejected */ START_TEST(test_ns_bad_attr_leafname) { const char *text = ""; expect_failure(text, XML_ERROR_INVALID_TOKEN, "Invalid character in leafname not faulted"); } END_TEST START_TEST(test_ns_bad_element_leafname) { const char *text = ""; expect_failure(text, XML_ERROR_INVALID_TOKEN, "Invalid character in element leafname not faulted"); } END_TEST /* Test high-byte-set UTF-16 characters are valid in a leafname */ START_TEST(test_ns_utf16_leafname) { const char text[] = /* * where {KHO KHWAI} = U+0E04 = 0xe0 0xb8 0x84 in UTF-8 */ "<\0n\0:\0e\0 \0x\0m\0l\0n\0s\0:\0n\0=\0'\0U\0R\0I\0'\0 \0" "n\0:\0\x04\x0e=\0'\0a\0'\0 \0/\0>\0"; const XML_Char *expected = XCS("a"); CharData storage; CharData_Init(&storage); XML_SetStartElementHandler(g_parser, accumulate_attribute); XML_SetUserData(g_parser, &storage); if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE) == XML_STATUS_ERROR) xml_failure(g_parser); CharData_CheckXMLChars(&storage, expected); } END_TEST START_TEST(test_ns_utf16_element_leafname) { const char text[] = /* * where {KHO KHWAI} = U+0E04 = 0xe0 0xb8 0x84 in UTF-8 */ "\0<\0n\0:\x0e\x04\0 \0x\0m\0l\0n\0s\0:\0n\0=\0'\0U\0R\0I\0'\0/\0>"; #ifdef XML_UNICODE const XML_Char *expected = XCS("URI \x0e04"); #else const XML_Char *expected = XCS("URI \xe0\xb8\x84"); #endif CharData storage; CharData_Init(&storage); XML_SetStartElementHandler(g_parser, start_element_event_handler); XML_SetUserData(g_parser, &storage); if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE) == XML_STATUS_ERROR) xml_failure(g_parser); CharData_CheckXMLChars(&storage, expected); } END_TEST START_TEST(test_ns_utf16_doctype) { const char text[] = /* ]>\n * where {KHO KHWAI} = U+0E04 = 0xe0 0xb8 0x84 in UTF-8 */ "\0<\0!\0D\0O\0C\0T\0Y\0P\0E\0 \0f\0o\0o\0:\x0e\x04\0 " "\0[\0 \0<\0!\0E\0N\0T\0I\0T\0Y\0 \0b\0a\0r\0 \0'\0b\0a\0z\0'\0>\0 " "\0]\0>\0\n" /* &bar; */ "\0<\0f\0o\0o\0:\x0e\x04\0 " "\0x\0m\0l\0n\0s\0:\0f\0o\0o\0=\0'\0U\0R\0I\0'\0>" "\0&\0b\0a\0r\0;" "\0<\0/\0f\0o\0o\0:\x0e\x04\0>"; #ifdef XML_UNICODE const XML_Char *expected = XCS("URI \x0e04"); #else const XML_Char *expected = XCS("URI \xe0\xb8\x84"); #endif CharData storage; CharData_Init(&storage); XML_SetUserData(g_parser, &storage); XML_SetStartElementHandler(g_parser, start_element_event_handler); XML_SetUnknownEncodingHandler(g_parser, MiscEncodingHandler, NULL); if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)sizeof(text) - 1, XML_TRUE) == XML_STATUS_ERROR) xml_failure(g_parser); CharData_CheckXMLChars(&storage, expected); } END_TEST START_TEST(test_ns_invalid_doctype) { const char *text = "\n" "&bar;"; expect_failure(text, XML_ERROR_INVALID_TOKEN, "Invalid character in document local name not faulted"); } END_TEST START_TEST(test_ns_double_colon_doctype) { const char *text = "\n" "&bar;"; expect_failure(text, XML_ERROR_SYNTAX, "Double colon in document name not faulted"); } END_TEST START_TEST(test_ns_separator_in_uri) { struct test_case { enum XML_Status expectedStatus; const char *doc; XML_Char namesep; }; struct test_case cases[] = { {XML_STATUS_OK, "", XCS('\n')}, {XML_STATUS_ERROR, "", XCS('\n')}, {XML_STATUS_OK, "", XCS(':')}, }; size_t i = 0; size_t failCount = 0; for (; i < sizeof(cases) / sizeof(cases[0]); i++) { set_subtest("%s", cases[i].doc); XML_Parser parser = XML_ParserCreateNS(NULL, cases[i].namesep); XML_SetElementHandler(parser, dummy_start_element, dummy_end_element); if (_XML_Parse_SINGLE_BYTES(parser, cases[i].doc, (int)strlen(cases[i].doc), /*isFinal*/ XML_TRUE) != cases[i].expectedStatus) { failCount++; } XML_ParserFree(parser); } if (failCount) { fail("Namespace separator handling is broken"); } } END_TEST void make_namespace_test_case(Suite *s) { TCase *tc_namespace = tcase_create("XML namespaces"); suite_add_tcase(s, tc_namespace); tcase_add_checked_fixture(tc_namespace, namespace_setup, namespace_teardown); tcase_add_test(tc_namespace, test_return_ns_triplet); tcase_add_test(tc_namespace, test_ns_parser_reset); tcase_add_test(tc_namespace, test_ns_tagname_overwrite); tcase_add_test(tc_namespace, test_ns_tagname_overwrite_triplet); tcase_add_test(tc_namespace, test_start_ns_clears_start_element); tcase_add_test__ifdef_xml_dtd(tc_namespace, test_default_ns_from_ext_subset_and_ext_ge); tcase_add_test(tc_namespace, test_ns_prefix_with_empty_uri_1); tcase_add_test(tc_namespace, test_ns_prefix_with_empty_uri_2); tcase_add_test(tc_namespace, test_ns_prefix_with_empty_uri_3); tcase_add_test(tc_namespace, test_ns_prefix_with_empty_uri_4); tcase_add_test(tc_namespace, test_ns_unbound_prefix); tcase_add_test(tc_namespace, test_ns_default_with_empty_uri); tcase_add_test(tc_namespace, test_ns_duplicate_attrs_diff_prefixes); tcase_add_test(tc_namespace, test_ns_duplicate_hashes); tcase_add_test(tc_namespace, test_ns_unbound_prefix_on_attribute); tcase_add_test(tc_namespace, test_ns_unbound_prefix_on_element); tcase_add_test(tc_namespace, test_ns_long_element); tcase_add_test(tc_namespace, test_ns_mixed_prefix_atts); tcase_add_test(tc_namespace, test_ns_extend_uri_buffer); tcase_add_test(tc_namespace, test_ns_reserved_attributes); tcase_add_test(tc_namespace, test_ns_reserved_attributes_2); tcase_add_test(tc_namespace, test_ns_extremely_long_prefix); tcase_add_test(tc_namespace, test_ns_unknown_encoding_success); tcase_add_test(tc_namespace, test_ns_double_colon); tcase_add_test(tc_namespace, test_ns_double_colon_element); tcase_add_test(tc_namespace, test_ns_bad_attr_leafname); tcase_add_test(tc_namespace, test_ns_bad_element_leafname); tcase_add_test(tc_namespace, test_ns_utf16_leafname); tcase_add_test(tc_namespace, test_ns_utf16_element_leafname); tcase_add_test__if_xml_ge(tc_namespace, test_ns_utf16_doctype); tcase_add_test(tc_namespace, test_ns_invalid_doctype); tcase_add_test(tc_namespace, test_ns_double_colon_doctype); tcase_add_test(tc_namespace, test_ns_separator_in_uri); }