From 044e3a8af47a7624c2a58bdf5a1a6833686ea02a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Andr=C3=A9=20dos=20Santos=20Lopes?= Date: Wed, 4 Mar 2026 16:28:53 +0000 Subject: [PATCH 1/2] Upgrade libxml2 --- appsec/third_party/libxml2/VERSION | 2 +- appsec/third_party/libxml2/src/CMakeLists.txt | 89 ++++++++++++++++++- appsec/third_party/libxml2/src/VERSION | 2 +- appsec/third_party/libxml2/src/encoding.c | 3 +- .../libxml2/src/include/libxml/catalog.h | 2 + .../libxml2/src/include/libxml/relaxng.h | 4 + .../libxml2/src/include/private/threads.h | 11 ++- appsec/third_party/libxml2/src/parser.c | 2 +- appsec/third_party/libxml2/src/tree.c | 4 +- appsec/third_party/libxml2/src/uri.c | 14 +-- appsec/third_party/libxml2/src/xmlIO.c | 12 ++- appsec/third_party/libxml2/src/xmlstring.c | 4 +- appsec/third_party/libxml2/vendor_libxml2.py | 12 +++ 13 files changed, 142 insertions(+), 19 deletions(-) diff --git a/appsec/third_party/libxml2/VERSION b/appsec/third_party/libxml2/VERSION index 3b1fc7950fa..07d875c2d2a 100644 --- a/appsec/third_party/libxml2/VERSION +++ b/appsec/third_party/libxml2/VERSION @@ -1 +1 @@ -2.15.1 +2.15.2 diff --git a/appsec/third_party/libxml2/src/CMakeLists.txt b/appsec/third_party/libxml2/src/CMakeLists.txt index 116c77dd210..fb6d3f77c19 100644 --- a/appsec/third_party/libxml2/src/CMakeLists.txt +++ b/appsec/third_party/libxml2/src/CMakeLists.txt @@ -169,6 +169,28 @@ if(LIBXML2_WITH_TLS) endif() endif() +# Headers which are private. +set( + LIBXML2_PRIVATE_HDRS + include/private/buf.h + include/private/cata.h + include/private/dict.h + include/private/enc.h + include/private/entities.h + include/private/error.h + include/private/globals.h + include/private/html.h + include/private/io.h + include/private/memory.h + include/private/parser.h + include/private/regexp.h + include/private/save.h + include/private/string.h + include/private/threads.h + include/private/tree.h + include/private/xpath.h +) + set( LIBXML2_HDRS include/libxml/c14n.h @@ -240,6 +262,7 @@ set( xmlmemory.c xmlstring.c ) + if(LIBXML2_WITH_C14N) list(APPEND LIBXML2_SRCS c14n.c) endif() @@ -304,7 +327,7 @@ if(WIN32) ) endif() -add_library(LibXml2 ${LIBXML2_HDRS} ${LIBXML2_SRCS}) +add_library(LibXml2 ${LIBXML2_HDRS} ${LIBXML2_SRCS} ${LIBXML2_PRIVATE_HDRS}) add_library(LibXml2::LibXml2 ALIAS LibXml2) target_include_directories( @@ -455,6 +478,9 @@ if(LIBXML2_WITH_PROGRAMS) endif() install(TARGETS ${PROGRAM} EXPORT LibXml2 RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT programs) endforeach() + if(LIBXML2_WITH_ZLIB) + target_link_libraries(xmllint ZLIB::ZLIB) + endif() endif() if(LIBXML2_WITH_TESTS) @@ -465,6 +491,7 @@ if(LIBXML2_WITH_TESTS) runxmlconf runsuite testapi + testcatalog testchar testdict testModule @@ -487,10 +514,67 @@ 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}) add_test(NAME testrecurse COMMAND testrecurse WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + + + function(ADD_SUITE test_dir program) + file(GLOB_RECURSE files_to_process CONFIGURE_DEPENDS "./test/${test_dir}/*.script") + + foreach(file_path ${files_to_process}) + file(RELATIVE_PATH file_relative "${CMAKE_CURRENT_SOURCE_DIR}" "${file_path}") + get_filename_component(dir_path ${file_relative} DIRECTORY) + get_filename_component(base_name ${file_relative} NAME_WE) + get_filename_component(extension ${file_relative} EXT) + + set(actual_stdout "${CMAKE_CURRENT_BINARY_DIR}/result/${test_dir}/${base_name}") + set(actual_stderr "${CMAKE_CURRENT_BINARY_DIR}/result/${test_dir}/${base_name}.err") + set(expected_stdout "${CMAKE_CURRENT_SOURCE_DIR}/result/${test_dir}/${base_name}") + set(expected_stderr "${CMAKE_CURRENT_SOURCE_DIR}/result/${test_dir}/${base_name}.err") + + set(xml_path "${CMAKE_CURRENT_SOURCE_DIR}/${dir_path}/${base_name}.xml") + set(sgml_path "${CMAKE_CURRENT_SOURCE_DIR}/${dir_path}/${base_name}.sgml") + + if(EXISTS "${xml_path}") + set(input_file "./${dir_path}/${base_name}.xml") + elseif(EXISTS "${sgml_path}") + set(input_file "./${dir_path}/${base_name}.sgml") + else() + message(FATAL_ERROR "Neither XML nor SGML exists for: ${dir_path}/${base_name}") + endif() + + add_test( + NAME ${test_dir}_${base_name} + COMMAND ${CMAKE_COMMAND} + -DPROG=$ + -DSTDIN=./${dir_path}/${base_name}.script + -DXML=${input_file} + -DSTDOUT=${actual_stdout} + -DSTDERR=${actual_stderr} + -DEXPECTED_STDOUT=${expected_stdout} + -DEXPECTED_STDERR=${expected_stderr} + -DWORKDIR=${CMAKE_CURRENT_SOURCE_DIR} + -P ${CMAKE_CURRENT_SOURCE_DIR}/run_and_diff.cmake + ) + + endforeach() + endfunction() + + add_suite("scripts" xmllint) + add_suite("catalogs" xmlcatalog) + + add_test( + NAME catalog_add_del + COMMAND ${CMAKE_COMMAND} + -DXMLCAT=$ + -DEXPECTED_FULL=${CMAKE_CURRENT_SOURCE_DIR}/result/catalogs/mycatalog.full + -DEXPECTED_EMPTY=${CMAKE_CURRENT_SOURCE_DIR}/result/catalogs/mycatalog.empty + -P ${CMAKE_CURRENT_SOURCE_DIR}/test_xmlcatalog_add_del.cmake + ) + endif() if(LIBXML2_WITH_DOCS OR LIBXML2_WITH_PYTHON) @@ -647,6 +731,9 @@ configure_file(config.h.cmake.in config.h) configure_file(include/libxml/xmlversion.h.in libxml/xmlversion.h) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libxml/xmlversion.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/libxml2/libxml COMPONENT development) +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} + FILES ${LIBXML2_SRCS} ${LIBXML2_HDRS} ${LIBXML2_PRIVATE_HDRS}) + if(LIBXML2_WITH_PYTHON) set(prefix "${CMAKE_INSTALL_PREFIX}") configure_file(python/setup.py.in setup.py @ONLY) diff --git a/appsec/third_party/libxml2/src/VERSION b/appsec/third_party/libxml2/src/VERSION index 3b1fc7950fa..07d875c2d2a 100644 --- a/appsec/third_party/libxml2/src/VERSION +++ b/appsec/third_party/libxml2/src/VERSION @@ -1 +1 @@ -2.15.1 +2.15.2 diff --git a/appsec/third_party/libxml2/src/encoding.c b/appsec/third_party/libxml2/src/encoding.c index 7dfd37aafb7..3d99e84828f 100644 --- a/appsec/third_party/libxml2/src/encoding.c +++ b/appsec/third_party/libxml2/src/encoding.c @@ -1309,7 +1309,8 @@ xmlEncodingMatch(const char *name1, const char *name2) { name2 += 1; } - return((*name1 == 0) && (*name2 == 0)); + /* Only check whether name1 starts with name2 */ + return(*name2 == 0); } #endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */ diff --git a/appsec/third_party/libxml2/src/include/libxml/catalog.h b/appsec/third_party/libxml2/src/include/libxml/catalog.h index 88a7483cdc5..e1bc5feb763 100644 --- a/appsec/third_party/libxml2/src/include/libxml/catalog.h +++ b/appsec/third_party/libxml2/src/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/appsec/third_party/libxml2/src/include/libxml/relaxng.h b/appsec/third_party/libxml2/src/include/libxml/relaxng.h index eafc6604d0f..099dacd80a2 100644 --- a/appsec/third_party/libxml2/src/include/libxml/relaxng.h +++ b/appsec/third_party/libxml2/src/include/libxml/relaxng.h @@ -136,6 +136,10 @@ XMLPUBFUN int xmlRelaxParserSetFlag (xmlRelaxNGParserCtxt *ctxt, int flag); +XMLPUBFUN int + xmlRelaxParserSetIncLImit (xmlRelaxNGParserCtxt *ctxt, + int limit); + XMLPUBFUN void xmlRelaxNGFreeParserCtxt (xmlRelaxNGParserCtxt *ctxt); XMLPUBFUN void diff --git a/appsec/third_party/libxml2/src/include/private/threads.h b/appsec/third_party/libxml2/src/include/private/threads.h index bb187869c76..823e325ee1f 100644 --- a/appsec/third_party/libxml2/src/include/private/threads.h +++ b/appsec/third_party/libxml2/src/include/private/threads.h @@ -7,9 +7,16 @@ #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #ifdef _WIN32_WINNT - #undef _WIN32_WINNT + #if _WIN32_WINNT < 0x0600 + #undef _WIN32_WINNT + #endif + #else + /* get the default version of the SDK */ + #include + #endif + #ifndef _WIN32_WINNT + #define _WIN32_WINNT 0x0600 #endif - #define _WIN32_WINNT 0x0600 #include #define HAVE_WIN32_THREADS #else diff --git a/appsec/third_party/libxml2/src/parser.c b/appsec/third_party/libxml2/src/parser.c index c7f46a31a73..6e7621a86b5 100644 --- a/appsec/third_party/libxml2/src/parser.c +++ b/appsec/third_party/libxml2/src/parser.c @@ -12033,7 +12033,7 @@ xmlCtxtParseContent(xmlParserCtxt *ctxt, xmlParserInput *input, case XML_ENTITY_REF_NODE: case XML_PI_NODE: case XML_COMMENT_NODE: - for (cur = node->parent; cur != NULL; cur = node->parent) { + for (cur = node->parent; cur != NULL; cur = cur->parent) { if ((cur->type == XML_ELEMENT_NODE) || (cur->type == XML_DOCUMENT_NODE) || (cur->type == XML_HTML_DOCUMENT_NODE)) { diff --git a/appsec/third_party/libxml2/src/tree.c b/appsec/third_party/libxml2/src/tree.c index e4fd0d6e587..ef63a68d713 100644 --- a/appsec/third_party/libxml2/src/tree.c +++ b/appsec/third_party/libxml2/src/tree.c @@ -2445,11 +2445,11 @@ xmlRemoveEntity(xmlEntityPtr ent) { (ent->etype == XML_EXTERNAL_PARAMETER_ENTITY)) { if (intSubset != NULL) { if (xmlHashLookup(intSubset->pentities, ent->name) == ent) - xmlHashRemoveEntry(intSubset->entities, ent->name, NULL); + xmlHashRemoveEntry(intSubset->pentities, ent->name, NULL); } if (extSubset != NULL) { if (xmlHashLookup(extSubset->pentities, ent->name) == ent) - xmlHashRemoveEntry(extSubset->entities, ent->name, NULL); + xmlHashRemoveEntry(extSubset->pentities, ent->name, NULL); } } } diff --git a/appsec/third_party/libxml2/src/uri.c b/appsec/third_party/libxml2/src/uri.c index cfeca1e0120..f94473b431d 100644 --- a/appsec/third_party/libxml2/src/uri.c +++ b/appsec/third_party/libxml2/src/uri.c @@ -2491,9 +2491,9 @@ xmlBuildRelativeURISafe(const xmlChar * URI, const xmlChar * base, { xmlChar *val = NULL; int ret = 0; - int ix; - int nbslash = 0; - int len; + size_t ix = 0; + size_t nbslash = 0; + size_t len = 0; xmlURIPtr ref = NULL; xmlURIPtr bas = NULL; const xmlChar *bptr, *uptr, *rptr; @@ -2574,7 +2574,7 @@ xmlBuildRelativeURISafe(const xmlChar * URI, const xmlChar * base, * At this point we can compare the two paths */ { - int pos = 0; + size_t pos = 0; /* * Next we compare the two strings and find where they first differ @@ -2618,7 +2618,7 @@ xmlBuildRelativeURISafe(const xmlChar * URI, const xmlChar * base, goto done; } - len = xmlStrlen (uptr) + 1; + len = (size_t) xmlStrlen (uptr) + 1; } if (nbslash == 0) { @@ -2636,6 +2636,10 @@ xmlBuildRelativeURISafe(const xmlChar * URI, const xmlChar * base, * length of the remainder of the URI, plus enough space * for the "../" groups, plus one for the terminator */ + if (len + 3 * nbslash > SIZE_MAX) { + ret = -1; + goto done; + } val = (xmlChar *) xmlMalloc (len + 3 * nbslash); if (val == NULL) { ret = -1; diff --git a/appsec/third_party/libxml2/src/xmlIO.c b/appsec/third_party/libxml2/src/xmlIO.c index 789fc5e72cd..ebe9b7c4ee3 100644 --- a/appsec/third_party/libxml2/src/xmlIO.c +++ b/appsec/third_party/libxml2/src/xmlIO.c @@ -265,7 +265,7 @@ xmlEscapeText(const xmlChar *string, int flags) { if (totalSize > size - used) { xmlChar *tmp; - int newSize; + size_t newSize; if ((size > (SIZE_MAX - 1) / 2) || (totalSize > (SIZE_MAX - 1) / 2 - size)) { @@ -1824,9 +1824,12 @@ xmlOutputBufferCreateBuffer(xmlBuffer *buffer, */ const xmlChar * xmlOutputBufferGetContent(xmlOutputBuffer *out) { - if ((out == NULL) || (out->buffer == NULL) || (out->error != 0)) + if ((out == NULL) || (out->buffer == NULL) || ((out->encoder != NULL) && (out->conv == NULL)) || (out->error != 0)) return(NULL); + if (out->encoder != NULL) + return(xmlBufContent(out->conv)); + return(xmlBufContent(out->buffer)); } @@ -1838,9 +1841,12 @@ xmlOutputBufferGetContent(xmlOutputBuffer *out) { */ size_t xmlOutputBufferGetSize(xmlOutputBuffer *out) { - if ((out == NULL) || (out->buffer == NULL) || (out->error != 0)) + if ((out == NULL) || (out->buffer == NULL) || ((out->encoder != NULL) && (out->conv == NULL)) || (out->error != 0)) return(0); + if (out->encoder != NULL) + return(xmlBufUse(out->conv)); + return(xmlBufUse(out->buffer)); } diff --git a/appsec/third_party/libxml2/src/xmlstring.c b/appsec/third_party/libxml2/src/xmlstring.c index 6a598c611df..9dd5a874383 100644 --- a/appsec/third_party/libxml2/src/xmlstring.c +++ b/appsec/third_party/libxml2/src/xmlstring.c @@ -517,7 +517,7 @@ xmlStrPrintf(xmlChar *buf, int len, const char *msg, ...) { va_list args; int ret; - if((buf == NULL) || (msg == NULL)) { + if((buf == NULL) || (msg == NULL) || (len <= 0)) { return(-1); } @@ -542,7 +542,7 @@ int xmlStrVPrintf(xmlChar *buf, int len, const char *msg, va_list ap) { int ret; - if((buf == NULL) || (msg == NULL)) { + if((buf == NULL) || (msg == NULL) || (len <= 0)) { return(-1); } diff --git a/appsec/third_party/libxml2/vendor_libxml2.py b/appsec/third_party/libxml2/vendor_libxml2.py index ccd7d8b34a3..5b49a8c671e 100755 --- a/appsec/third_party/libxml2/vendor_libxml2.py +++ b/appsec/third_party/libxml2/vendor_libxml2.py @@ -206,6 +206,18 @@ def extract_needed_files(tarball: Path, dest_dir: Path) -> None: print(f"Extracted {extracted_count} files") + patch_cmake(dest_dir) + + +def patch_cmake(dest_dir: Path) -> None: + """Remove private headers we don't vendor from LIBXML2_PRIVATE_HDRS.""" + cmake_path = dest_dir / "CMakeLists.txt" + content = cmake_path.read_text() + for header in EXCLUDED_PRIVATE_HEADERS: + content = content.replace(f" {header}\n", "") + cmake_path.write_text(content) + print(f"Patched CMakeLists.txt: removed {EXCLUDED_PRIVATE_HEADERS}") + def get_version_url(version: str) -> str: """Get the download URL for a given libxml2 version.""" From 72465924c569c0d86bc5911e8367e9345fb88484 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Andr=C3=A9=20dos=20Santos=20Lopes?= Date: Wed, 4 Mar 2026 17:42:58 +0000 Subject: [PATCH 2/2] Cache gradle dist to avoid transient ci failures --- .gitlab/generate-appsec.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab/generate-appsec.php b/.gitlab/generate-appsec.php index e0ca3922c29..b768a4dd473 100644 --- a/.gitlab/generate-appsec.php +++ b/.gitlab/generate-appsec.php @@ -101,6 +101,7 @@ KUBERNETES_MEMORY_REQUEST: 24Gi KUBERNETES_MEMORY_LIMIT: 30Gi ARCH: amd64 + GRADLE_USER_HOME: "$CI_PROJECT_DIR/.gradle-home" parallel: matrix: - targets: @@ -157,6 +158,7 @@ - key: "appsec int test cache" paths: - appsec/tests/integration/build/*.tar.gz + - .gradle-home/wrapper/dists/ "appsec code coverage": stage: test