From f8399e62a31095bf1ced01827c33f9b29494046f Mon Sep 17 00:00:00 2001 From: Daniel Garcia Moreno Date: Fri, 19 Dec 2025 12:27:54 +0100 Subject: [PATCH] testcatalog: Add new tests for catalog.c Adds a new test program to run specific tests related to catalog parsing. This initial version includes a couple of tests, the first one to check the infinite recursion detection related to: https://gitlab.gnome.org/GNOME/libxml2/-/issues/1018. The second one tests the nextCatalog element repeated parsing, related to: https://gitlab.gnome.org/GNOME/libxml2/-/issues/1019 https://gitlab.gnome.org/GNOME/libxml2/-/issues/1040 --- CMakeLists.txt | 2 + Makefile.am | 6 ++ catalog.c | 63 +++++++++++----- include/libxml/catalog.h | 2 + meson.build | 1 + test/catalogs/catalog-recursive.xml | 3 + test/catalogs/repeated-next-catalog.xml | 10 +++ testcatalog.c | 96 +++++++++++++++++++++++++ 8 files changed, 164 insertions(+), 19 deletions(-) create mode 100644 test/catalogs/catalog-recursive.xml create mode 100644 test/catalogs/repeated-next-catalog.xml create mode 100644 testcatalog.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 163661f81..7d5702df8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -498,6 +498,7 @@ if(LIBXML2_WITH_TESTS) runxmlconf runsuite testapi + testcatalog testchar testdict testModule @@ -520,6 +521,7 @@ if(LIBXML2_WITH_TESTS) add_test(NAME runxmlconf COMMAND runxmlconf WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) endif() add_test(NAME testapi COMMAND testapi) + add_test(NAME testcatalog COMMAND testcatalog) add_test(NAME testchar COMMAND testchar) add_test(NAME testdict COMMAND testdict) add_test(NAME testparser COMMAND testparser WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/Makefile.am b/Makefile.am index c51dfd8ef..c794eac8b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -23,6 +23,7 @@ check_PROGRAMS = \ runxmlconf \ testModule \ testapi \ + testcatalog \ testchar \ testdict \ testlimits \ @@ -120,6 +121,10 @@ testlimits_SOURCES=testlimits.c testlimits_DEPENDENCIES = $(DEPS) testlimits_LDADD= $(LDADDS) +testcatalog_SOURCES=testcatalog.c +testcatalog_DEPENDENCIES = $(DEPS) +testcatalog_LDADD= $(LDADDS) + testchar_SOURCES=testchar.c testchar_DEPENDENCIES = $(DEPS) testchar_LDADD= $(LDADDS) @@ -167,6 +172,7 @@ check-local: $(CHECKER) ./runtest$(EXEEXT) $(CHECKER) ./testrecurse$(EXEEXT) $(CHECKER) ./testapi$(EXEEXT) + $(CHECKER) ./testcatalog$(EXEEXT) $(CHECKER) ./testchar$(EXEEXT) $(CHECKER) ./testdict$(EXEEXT) $(CHECKER) ./testparser$(EXEEXT) diff --git a/catalog.c b/catalog.c index 401dbc14f..eb8891626 100644 --- a/catalog.c +++ b/catalog.c @@ -640,43 +640,54 @@ static void xmlDumpXMLCatalogNode(xmlCatalogEntryPtr catal, xmlNodePtr catalog, } } -static int -xmlDumpXMLCatalog(FILE *out, xmlCatalogEntryPtr catal) { - int ret; - xmlDocPtr doc; +static xmlDocPtr +xmlDumpXMLCatalogToDoc(xmlCatalogEntryPtr catal) { xmlNsPtr ns; xmlDtdPtr dtd; xmlNodePtr catalog; - xmlOutputBufferPtr buf; + xmlDocPtr doc = xmlNewDoc(NULL); + if (doc == NULL) { + return(NULL); + } - /* - * Rebuild a catalog - */ - doc = xmlNewDoc(NULL); - if (doc == NULL) - return(-1); dtd = xmlNewDtd(doc, BAD_CAST "catalog", - BAD_CAST "-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN", -BAD_CAST "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd"); + BAD_CAST "-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN", + BAD_CAST "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd"); xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd); ns = xmlNewNs(NULL, XML_CATALOGS_NAMESPACE, NULL); if (ns == NULL) { - xmlFreeDoc(doc); - return(-1); + xmlFreeDoc(doc); + return(NULL); } catalog = xmlNewDocNode(doc, ns, BAD_CAST "catalog", NULL); if (catalog == NULL) { - xmlFreeNs(ns); - xmlFreeDoc(doc); - return(-1); + xmlFreeDoc(doc); + xmlFreeNs(ns); + return(NULL); } catalog->nsDef = ns; xmlAddChild((xmlNodePtr) doc, catalog); - xmlDumpXMLCatalogNode(catal, catalog, doc, ns, NULL); + return(doc); +} + +static int +xmlDumpXMLCatalog(FILE *out, xmlCatalogEntryPtr catal) { + int ret; + xmlDocPtr doc; + xmlOutputBufferPtr buf; + + /* + * Rebuild a catalog + */ + doc = xmlDumpXMLCatalogToDoc(catal); + if (doc == NULL) { + return(-1); + } + /* * reserialize it */ @@ -3352,6 +3363,20 @@ xmlCatalogDump(FILE *out) { xmlACatalogDump(xmlDefaultCatalog, out); } + +/** + * Dump all the global catalog content as a xmlDoc + * This function is just for testing/debugging purposes + * + * @returns The catalog as xmlDoc or NULL if failed, it must be freed by the caller. + */ +xmlDocPtr +xmlCatalogDumpDoc(void) { + if (!xmlCatalogInitialized) + xmlInitializeCatalog(); + + return xmlDumpXMLCatalogToDoc(xmlDefaultCatalog->xml); +} #endif /* LIBXML_OUTPUT_ENABLED */ /** diff --git a/include/libxml/catalog.h b/include/libxml/catalog.h index 88a7483cd..e1bc5feb7 100644 --- a/include/libxml/catalog.h +++ b/include/libxml/catalog.h @@ -138,6 +138,8 @@ XMLPUBFUN void #ifdef LIBXML_OUTPUT_ENABLED XMLPUBFUN void xmlCatalogDump (FILE *out); +XMLPUBFUN xmlDocPtr + xmlCatalogDumpDoc (void); #endif /* LIBXML_OUTPUT_ENABLED */ XMLPUBFUN xmlChar * xmlCatalogResolve (const xmlChar *pubID, diff --git a/meson.build b/meson.build index 1cd89f09c..4bf17f6c4 100644 --- a/meson.build +++ b/meson.build @@ -517,6 +517,7 @@ checks = { # Disabled for now, see #694 # 'testModule': [], 'testapi': [], + 'testcatalog': [], 'testchar': [], 'testdict': [], 'testlimits': [], diff --git a/test/catalogs/catalog-recursive.xml b/test/catalogs/catalog-recursive.xml new file mode 100644 index 000000000..3b3d03f98 --- /dev/null +++ b/test/catalogs/catalog-recursive.xml @@ -0,0 +1,3 @@ + + + diff --git a/test/catalogs/repeated-next-catalog.xml b/test/catalogs/repeated-next-catalog.xml new file mode 100644 index 000000000..76d34c3c8 --- /dev/null +++ b/test/catalogs/repeated-next-catalog.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/testcatalog.c b/testcatalog.c new file mode 100644 index 000000000..86d33bd0d --- /dev/null +++ b/testcatalog.c @@ -0,0 +1,96 @@ +/* + * testcatalog.c: C program to run libxml2 catalog.c unit tests + * + * To compile on Unixes: + * cc -o testcatalog `xml2-config --cflags` testcatalog.c `xml2-config --libs` -lpthread + * + * See Copyright for the status of this software. + * + * Author: Daniel Garcia + */ + + +#include "libxml.h" +#include + +#ifdef LIBXML_CATALOG_ENABLED +#include + +/* Test catalog resolve uri with recursive catalog */ +static int +testRecursiveDelegateUri(void) { + int ret = 0; + const char *cat = "test/catalogs/catalog-recursive.xml"; + const char *entity = "/foo.ent"; + xmlChar *resolved = NULL; + + xmlInitParser(); + xmlLoadCatalog(cat); + + /* This should trigger recursive error */ + resolved = xmlCatalogResolveURI(BAD_CAST entity); + if (resolved != NULL) { + fprintf(stderr, "CATALOG-FAILURE: Catalog %s entity should fail to resolve\n", entity); + ret = 1; + } + xmlCatalogCleanup(); + + return ret; +} + +/* Test parsing repeated NextCatalog */ +static int +testRepeatedNextCatalog(void) { + int ret = 0; + int i = 0; + const char *cat = "test/catalogs/repeated-next-catalog.xml"; + const char *entity = "/foo.ent"; + xmlDocPtr doc = NULL; + xmlNodePtr node = NULL; + + xmlInitParser(); + + xmlLoadCatalog(cat); + /* To force the complete recursive load */ + xmlCatalogResolveURI(BAD_CAST entity); + /** + * Ensure that the doc doesn't contain the same nextCatalog + */ + doc = xmlCatalogDumpDoc(); + xmlCatalogCleanup(); + + if (doc == NULL) { + fprintf(stderr, "CATALOG-FAILURE: Failed to dump the catalog\n"); + return 1; + } + + /* Just the root "catalog" node with a series of nextCatalog */ + node = xmlDocGetRootElement(doc); + node = node->children; + for (i=0; node != NULL; node=node->next, i++) {} + if (i > 1) { + fprintf(stderr, "CATALOG-FAILURE: Found %d nextCatalog entries and should be 1\n", i); + ret = 1; + } + + xmlFreeDoc(doc); + + return ret; +} + +int +main(void) { + int err = 0; + + err |= testRecursiveDelegateUri(); + err |= testRepeatedNextCatalog(); + + return err; +} +#else +/* No catalog, so everything okay */ +int +main(void) { + return 0; +} +#endif