diff --git a/SOURCES/expat-2.2.5-Add-missing-validation-of-encoding.patch b/SOURCES/expat-2.2.5-Add-missing-validation-of-encoding.patch
new file mode 100644
index 0000000..8876367
--- /dev/null
+++ b/SOURCES/expat-2.2.5-Add-missing-validation-of-encoding.patch
@@ -0,0 +1,200 @@
+commit e8f285b522a907603501329e5b4212755f525fdf
+Author: Tomas Korbar <tkorbar@redhat.com>
+Date:   Thu Mar 3 12:04:09 2022 +0100
+
+    CVE-2022-25235
+
+diff --git a/lib/xmltok.c b/lib/xmltok.c
+index 6b415d8..b55732a 100644
+--- a/lib/xmltok.c
++++ b/lib/xmltok.c
+@@ -103,13 +103,6 @@
+                       + ((((byte)[2]) >> 5) & 1)] \
+          & (1u << (((byte)[2]) & 0x1F)))
+ 
+-#define UTF8_GET_NAMING(pages, p, n) \
+-  ((n) == 2 \
+-  ? UTF8_GET_NAMING2(pages, (const unsigned char *)(p)) \
+-  : ((n) == 3 \
+-     ? UTF8_GET_NAMING3(pages, (const unsigned char *)(p)) \
+-     : 0))
+-
+ /* Detection of invalid UTF-8 sequences is based on Table 3.1B
+    of Unicode 3.2: http://www.unicode.org/unicode/reports/tr28/
+    with the additional restriction of not allowing the Unicode
+diff --git a/lib/xmltok_impl.c b/lib/xmltok_impl.c
+index 0403dd3..56d7a40 100644
+--- a/lib/xmltok_impl.c
++++ b/lib/xmltok_impl.c
+@@ -61,7 +61,7 @@
+    case BT_LEAD ## n: \
+      if (end - ptr < n) \
+        return XML_TOK_PARTIAL_CHAR; \
+-     if (!IS_NAME_CHAR(enc, ptr, n)) { \
++     if (IS_INVALID_CHAR(enc, ptr, n) || ! IS_NAME_CHAR(enc, ptr, n)) { \
+        *nextTokPtr = ptr; \
+        return XML_TOK_INVALID; \
+      } \
+@@ -89,7 +89,7 @@
+    case BT_LEAD ## n: \
+      if (end - ptr < n) \
+        return XML_TOK_PARTIAL_CHAR; \
+-     if (!IS_NMSTRT_CHAR(enc, ptr, n)) { \
++     if (IS_INVALID_CHAR(enc, ptr, n) || ! IS_NMSTRT_CHAR(enc, ptr, n)) { \
+        *nextTokPtr = ptr; \
+        return XML_TOK_INVALID; \
+      } \
+@@ -1117,6 +1117,10 @@ PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end,
+   case BT_LEAD ## n: \
+     if (end - ptr < n) \
+       return XML_TOK_PARTIAL_CHAR; \
++    if (IS_INVALID_CHAR(enc, ptr, n)) {                                        \
++      *nextTokPtr = ptr;                                                       \
++      return XML_TOK_INVALID;                                                  \
++    }                                                                          \
+     if (IS_NMSTRT_CHAR(enc, ptr, n)) { \
+       ptr += n; \
+       tok = XML_TOK_NAME; \
+diff --git a/tests/runtests.c b/tests/runtests.c
+index 278bfa1..0f3afde 100644
+--- a/tests/runtests.c
++++ b/tests/runtests.c
+@@ -6540,6 +6540,106 @@ START_TEST(test_utf8_in_cdata_section_2)
+ }
+ END_TEST
+ 
++START_TEST(test_utf8_in_start_tags) {
++  struct test_case {
++    bool goodName;
++    bool goodNameStart;
++    const char *tagName;
++  };
++
++  // The idea with the tests below is this:
++  // We want to cover 1-, 2- and 3-byte sequences, 4-byte sequences
++  // go to isNever and are hence not a concern.
++  //
++  // We start with a character that is a valid name character
++  // (or even name-start character, see XML 1.0r4 spec) and then we flip
++  // single bits at places where (1) the result leaves the UTF-8 encoding space
++  // and (2) we stay in the same n-byte sequence family.
++  //
++  // The flipped bits are highlighted in angle brackets in comments,
++  // e.g. "[<1>011 1001]" means we had [0011 1001] but we now flipped
++  // the most significant bit to 1 to leave UTF-8 encoding space.
++  struct test_case cases[] = {
++      // 1-byte UTF-8: [0xxx xxxx]
++      {true, true, "\x3A"},   // [0011 1010] = ASCII colon ':'
++      {false, false, "\xBA"}, // [<1>011 1010]
++      {true, false, "\x39"},  // [0011 1001] = ASCII nine '9'
++      {false, false, "\xB9"}, // [<1>011 1001]
++
++      // 2-byte UTF-8: [110x xxxx] [10xx xxxx]
++      {true, true, "\xDB\xA5"},   // [1101 1011] [1010 0101] =
++                                  // Arabic small waw U+06E5
++      {false, false, "\x9B\xA5"}, // [1<0>01 1011] [1010 0101]
++      {false, false, "\xDB\x25"}, // [1101 1011] [<0>010 0101]
++      {false, false, "\xDB\xE5"}, // [1101 1011] [1<1>10 0101]
++      {true, false, "\xCC\x81"},  // [1100 1100] [1000 0001] =
++                                  // combining char U+0301
++      {false, false, "\x8C\x81"}, // [1<0>00 1100] [1000 0001]
++      {false, false, "\xCC\x01"}, // [1100 1100] [<0>000 0001]
++      {false, false, "\xCC\xC1"}, // [1100 1100] [1<1>00 0001]
++
++      // 3-byte UTF-8: [1110 xxxx] [10xx xxxx] [10xxxxxx]
++      {true, true, "\xE0\xA4\x85"},   // [1110 0000] [1010 0100] [1000 0101] =
++                                      // Devanagari Letter A U+0905
++      {false, false, "\xA0\xA4\x85"}, // [1<0>10 0000] [1010 0100] [1000 0101]
++      {false, false, "\xE0\x24\x85"}, // [1110 0000] [<0>010 0100] [1000 0101]
++      {false, false, "\xE0\xE4\x85"}, // [1110 0000] [1<1>10 0100] [1000 0101]
++      {false, false, "\xE0\xA4\x05"}, // [1110 0000] [1010 0100] [<0>000 0101]
++      {false, false, "\xE0\xA4\xC5"}, // [1110 0000] [1010 0100] [1<1>00 0101]
++      {true, false, "\xE0\xA4\x81"},  // [1110 0000] [1010 0100] [1000 0001] =
++                                      // combining char U+0901
++      {false, false, "\xA0\xA4\x81"}, // [1<0>10 0000] [1010 0100] [1000 0001]
++      {false, false, "\xE0\x24\x81"}, // [1110 0000] [<0>010 0100] [1000 0001]
++      {false, false, "\xE0\xE4\x81"}, // [1110 0000] [1<1>10 0100] [1000 0001]
++      {false, false, "\xE0\xA4\x01"}, // [1110 0000] [1010 0100] [<0>000 0001]
++      {false, false, "\xE0\xA4\xC1"}, // [1110 0000] [1010 0100] [1<1>00 0001]
++  };
++  const bool atNameStart[] = {true, false};
++
++  size_t i = 0;
++  char doc[1024];
++  size_t failCount = 0;
++
++  for (; i < sizeof(cases) / sizeof(cases[0]); i++) {
++    size_t j = 0;
++    for (; j < sizeof(atNameStart) / sizeof(atNameStart[0]); j++) {
++      const bool expectedSuccess
++          = atNameStart[j] ? cases[i].goodNameStart : cases[i].goodName;
++      sprintf(doc, "<%s%s><!--", atNameStart[j] ? "" : "a", cases[i].tagName);
++      XML_Parser parser = XML_ParserCreate(NULL);
++
++      const enum XML_Status status
++          = XML_Parse(parser, doc, (int)strlen(doc), /*isFinal=*/XML_FALSE);
++
++      bool success = true;
++      if ((status == XML_STATUS_OK) != expectedSuccess) {
++        success = false;
++      }
++      if ((status == XML_STATUS_ERROR)
++          && (XML_GetErrorCode(parser) != XML_ERROR_INVALID_TOKEN)) {
++        success = false;
++      }
++
++      if (! success) {
++        fprintf(
++            stderr,
++            "FAIL case %2u (%sat name start, %u-byte sequence, error code %d)\n",
++            (unsigned)i + 1u, atNameStart[j] ? "    " : "not ",
++            (unsigned)strlen(cases[i].tagName), XML_GetErrorCode(parser));
++        failCount++;
++      }
++
++      XML_ParserFree(parser);
++    }
++  }
++
++  if (failCount > 0) {
++    fail("UTF-8 regression detected");
++  }
++}
++END_TEST
++
++
+ /* Test trailing spaces in elements are accepted */
+ static void XMLCALL
+ record_element_end_handler(void *userData,
+@@ -6734,6 +6834,15 @@ START_TEST(test_bad_doctype)
+ }
+ END_TEST
+ 
++START_TEST(test_bad_doctype_utf8) {
++  const char *text = "<!DOCTYPE \xDB\x25"
++                     "doc><doc/>"; // [1101 1011] [<0>010 0101]
++  expect_failure(text, XML_ERROR_INVALID_TOKEN,
++                 "Invalid UTF-8 in DOCTYPE not faulted");
++}
++END_TEST
++
++
+ START_TEST(test_bad_doctype_utf16)
+ {
+     const char text[] =
+@@ -12256,6 +12365,7 @@ make_suite(void)
+     tcase_add_test(tc_basic, test_ext_entity_utf8_non_bom);
+     tcase_add_test(tc_basic, test_utf8_in_cdata_section);
+     tcase_add_test(tc_basic, test_utf8_in_cdata_section_2);
++    tcase_add_test(tc_basic, test_utf8_in_start_tags);
+     tcase_add_test(tc_basic, test_trailing_spaces_in_elements);
+     tcase_add_test(tc_basic, test_utf16_attribute);
+     tcase_add_test(tc_basic, test_utf16_second_attr);
+@@ -12264,6 +12374,7 @@ make_suite(void)
+     tcase_add_test(tc_basic, test_bad_attr_desc_keyword);
+     tcase_add_test(tc_basic, test_bad_attr_desc_keyword_utf16);
+     tcase_add_test(tc_basic, test_bad_doctype);
++    tcase_add_test(tc_basic, test_bad_doctype_utf8);
+     tcase_add_test(tc_basic, test_bad_doctype_utf16);
+     tcase_add_test(tc_basic, test_bad_doctype_plus);
+     tcase_add_test(tc_basic, test_bad_doctype_star);
diff --git a/SOURCES/expat-2.2.5-Prevent-integer-overflow-in-copyString.patch b/SOURCES/expat-2.2.5-Prevent-integer-overflow-in-copyString.patch
new file mode 100644
index 0000000..f67a898
--- /dev/null
+++ b/SOURCES/expat-2.2.5-Prevent-integer-overflow-in-copyString.patch
@@ -0,0 +1,19 @@
+commit e5b609876e5a266725fba1c377b0ac95c737e6ed
+Author: Tomas Korbar <tkorbar@redhat.com>
+Date:   Mon May 2 12:44:06 2022 +0200
+
+    Fix CVE-2022-25314
+
+diff --git a/lib/xmlparse.c b/lib/xmlparse.c
+index 1f1413f..ceeec26 100644
+--- a/lib/xmlparse.c
++++ b/lib/xmlparse.c
+@@ -7525,7 +7525,7 @@ static XML_Char *
+ copyString(const XML_Char *s,
+            const XML_Memory_Handling_Suite *memsuite)
+ {
+-    int charsRequired = 0;
++    size_t charsRequired = 0;
+     XML_Char *result;
+ 
+     /* First determine how long the string is */
diff --git a/SOURCES/expat-2.2.5-Prevent-integer-overflow-in-storeRawNames.patch b/SOURCES/expat-2.2.5-Prevent-integer-overflow-in-storeRawNames.patch
new file mode 100644
index 0000000..be5927b
--- /dev/null
+++ b/SOURCES/expat-2.2.5-Prevent-integer-overflow-in-storeRawNames.patch
@@ -0,0 +1,31 @@
+commit 3a4141add108097fa548b196f5950c6663e1578e
+Author: Tomas Korbar <tkorbar@redhat.com>
+Date:   Thu Mar 3 13:50:20 2022 +0100
+
+    CVE-2022-25315
+
+diff --git a/lib/xmlparse.c b/lib/xmlparse.c
+index f0061c8..45fda00 100644
+--- a/lib/xmlparse.c
++++ b/lib/xmlparse.c
+@@ -2508,6 +2508,7 @@ storeRawNames(XML_Parser parser)
+   while (tag) {
+     int bufSize;
+     int nameLen = sizeof(XML_Char) * (tag->name.strLen + 1);
++    size_t rawNameLen;
+     char *rawNameBuf = tag->buf + nameLen;
+     /* Stop if already stored.  Since m_tagStack is a stack, we can stop
+        at the first entry that has already been copied; everything
+@@ -2519,7 +2520,11 @@ storeRawNames(XML_Parser parser)
+     /* For re-use purposes we need to ensure that the
+        size of tag->buf is a multiple of sizeof(XML_Char).
+     */
+-    bufSize = nameLen + ROUND_UP(tag->rawNameLength, sizeof(XML_Char));
++    rawNameLen = ROUND_UP(tag->rawNameLength, sizeof(XML_Char));
++    /* Detect and prevent integer overflow. */
++    if (rawNameLen > (size_t)INT_MAX - nameLen)
++      return XML_FALSE;
++    bufSize = nameLen + (int)rawNameLen;
+     if (bufSize > tag->bufEnd - tag->buf) {
+       char *temp = (char *)REALLOC(parser, tag->buf, bufSize);
+       if (temp == NULL)
diff --git a/SOURCES/expat-2.2.5-Prevent-stack-exhaustion-in-build_model.patch b/SOURCES/expat-2.2.5-Prevent-stack-exhaustion-in-build_model.patch
new file mode 100644
index 0000000..52af442
--- /dev/null
+++ b/SOURCES/expat-2.2.5-Prevent-stack-exhaustion-in-build_model.patch
@@ -0,0 +1,228 @@
+commit f1b61e6fbaedbb2bbea736269a015d97d4df46ce
+Author: Tomas Korbar <tkorbar@redhat.com>
+Date:   Tue May 3 13:42:54 2022 +0200
+
+    Fix CVE-2022-25313
+
+diff --git a/lib/xmlparse.c b/lib/xmlparse.c
+index ceeec26..d47e42c 100644
+--- a/lib/xmlparse.c
++++ b/lib/xmlparse.c
+@@ -7458,12 +7458,14 @@ build_node(XML_Parser parser,
+ }
+ 
+ static XML_Content *
+-build_model (XML_Parser parser)
+-{
+-  DTD * const dtd = parser->m_dtd;  /* save one level of indirection */
++build_model(XML_Parser parser) {
++  /* Function build_model transforms the existing parser->m_dtd->scaffold
++   * array of CONTENT_SCAFFOLD tree nodes into a new array of
++   * XML_Content tree nodes followed by a gapless list of zero-terminated
++   * strings. */
++  DTD *const dtd = parser->m_dtd; /* save one level of indirection */
+   XML_Content *ret;
+-  XML_Content *cpos;
+-  XML_Char * str;
++  XML_Char *str; /* the current string writing location */
+ 
+   /* Detect and prevent integer overflow.
+    * The preprocessor guard addresses the "always false" warning
+@@ -7486,13 +7488,99 @@ build_model (XML_Parser parser)
+                             + (dtd->contentStringLen * sizeof(XML_Char)));
+ 
+   ret = (XML_Content *)MALLOC(parser, allocsize);
+-  if (!ret)
++  if (! ret)
+     return NULL;
+ 
+-  str =  (XML_Char *) (&ret[dtd->scaffCount]);
+-  cpos = &ret[1];
++  /* What follows is an iterative implementation (of what was previously done
++   * recursively in a dedicated function called "build_node".  The old recursive
++   * build_node could be forced into stack exhaustion from input as small as a
++   * few megabyte, and so that was a security issue.  Hence, a function call
++   * stack is avoided now by resolving recursion.)
++   *
++   * The iterative approach works as follows:
++   *
++   * - We have two writing pointers, both walking up the result array; one does
++   *   the work, the other creates "jobs" for its colleague to do, and leads
++   *   the way:
++   *
++   *   - The faster one, pointer jobDest, always leads and writes "what job
++   *     to do" by the other, once they reach that place in the
++   *     array: leader "jobDest" stores the source node array index (relative
++   *     to array dtd->scaffold) in field "numchildren".
++   *
++   *   - The slower one, pointer dest, looks at the value stored in the
++   *     "numchildren" field (which actually holds a source node array index
++   *     at that time) and puts the real data from dtd->scaffold in.
++   *
++   * - Before the loop starts, jobDest writes source array index 0
++   *   (where the root node is located) so that dest will have something to do
++   *   when it starts operation.
++   *
++   * - Whenever nodes with children are encountered, jobDest appends
++   *   them as new jobs, in order.  As a result, tree node siblings are
++   *   adjacent in the resulting array, for example:
++   *
++   *     [0] root, has two children
++   *       [1] first child of 0, has three children
++   *         [3] first child of 1, does not have children
++   *         [4] second child of 1, does not have children
++   *         [5] third child of 1, does not have children
++   *       [2] second child of 0, does not have children
++   *
++   *   Or (the same data) presented in flat array view:
++   *
++   *     [0] root, has two children
++   *
++   *     [1] first child of 0, has three children
++   *     [2] second child of 0, does not have children
++   *
++   *     [3] first child of 1, does not have children
++   *     [4] second child of 1, does not have children
++   *     [5] third child of 1, does not have children
++   *
++   * - The algorithm repeats until all target array indices have been processed.
++   */
++  XML_Content *dest = ret; /* tree node writing location, moves upwards */
++  XML_Content *const destLimit = &ret[dtd->scaffCount];
++  XML_Content *jobDest = ret; /* next free writing location in target array */
++  str = (XML_Char *)&ret[dtd->scaffCount];
++
++  /* Add the starting job, the root node (index 0) of the source tree  */
++  (jobDest++)->numchildren = 0;
++
++  for (; dest < destLimit; dest++) {
++    /* Retrieve source tree array index from job storage */
++    const int src_node = (int)dest->numchildren;
++
++    /* Convert item */
++    dest->type = dtd->scaffold[src_node].type;
++    dest->quant = dtd->scaffold[src_node].quant;
++    if (dest->type == XML_CTYPE_NAME) {
++      const XML_Char *src;
++      dest->name = str;
++      src = dtd->scaffold[src_node].name;
++      for (;;) {
++        *str++ = *src;
++        if (! *src)
++          break;
++        src++;
++      }
++      dest->numchildren = 0;
++      dest->children = NULL;
++    } else {
++      unsigned int i;
++      int cn;
++      dest->name = NULL;
++      dest->numchildren = dtd->scaffold[src_node].childcnt;
++      dest->children = jobDest;
++
++      /* Append scaffold indices of children to array */
++      for (i = 0, cn = dtd->scaffold[src_node].firstchild;
++           i < dest->numchildren; i++, cn = dtd->scaffold[cn].nextsib)
++        (jobDest++)->numchildren = (unsigned int)cn;
++    }
++  }
+ 
+-  build_node(parser, 0, ret, &cpos, &str);
+   return ret;
+ }
+ 
+diff --git a/tests/runtests.c b/tests/runtests.c
+index eacd163..569ad8c 100644
+--- a/tests/runtests.c
++++ b/tests/runtests.c
+@@ -2848,6 +2848,81 @@ START_TEST(test_dtd_elements)
+ }
+ END_TEST
+ 
++static void XMLCALL
++element_decl_check_model(void *UNUSED_P(userData), const XML_Char *name,
++                         XML_Content *model) {
++  uint32_t errorFlags = 0;
++
++  /* Expected model array structure is this:
++   * [0] (type 6, quant 0)
++   *   [1] (type 5, quant 0)
++   *     [3] (type 4, quant 0, name "bar")
++   *     [4] (type 4, quant 0, name "foo")
++   *     [5] (type 4, quant 3, name "xyz")
++   *   [2] (type 4, quant 2, name "zebra")
++   */
++  errorFlags |= ((xcstrcmp(name, XCS("junk")) == 0) ? 0 : (1u << 0));
++  errorFlags |= ((model != NULL) ? 0 : (1u << 1));
++
++  errorFlags |= ((model[0].type == XML_CTYPE_SEQ) ? 0 : (1u << 2));
++  errorFlags |= ((model[0].quant == XML_CQUANT_NONE) ? 0 : (1u << 3));
++  errorFlags |= ((model[0].numchildren == 2) ? 0 : (1u << 4));
++  errorFlags |= ((model[0].children == &model[1]) ? 0 : (1u << 5));
++  errorFlags |= ((model[0].name == NULL) ? 0 : (1u << 6));
++
++  errorFlags |= ((model[1].type == XML_CTYPE_CHOICE) ? 0 : (1u << 7));
++  errorFlags |= ((model[1].quant == XML_CQUANT_NONE) ? 0 : (1u << 8));
++  errorFlags |= ((model[1].numchildren == 3) ? 0 : (1u << 9));
++  errorFlags |= ((model[1].children == &model[3]) ? 0 : (1u << 10));
++  errorFlags |= ((model[1].name == NULL) ? 0 : (1u << 11));
++
++  errorFlags |= ((model[2].type == XML_CTYPE_NAME) ? 0 : (1u << 12));
++  errorFlags |= ((model[2].quant == XML_CQUANT_REP) ? 0 : (1u << 13));
++  errorFlags |= ((model[2].numchildren == 0) ? 0 : (1u << 14));
++  errorFlags |= ((model[2].children == NULL) ? 0 : (1u << 15));
++  errorFlags |= ((xcstrcmp(model[2].name, XCS("zebra")) == 0) ? 0 : (1u << 16));
++
++  errorFlags |= ((model[3].type == XML_CTYPE_NAME) ? 0 : (1u << 17));
++  errorFlags |= ((model[3].quant == XML_CQUANT_NONE) ? 0 : (1u << 18));
++  errorFlags |= ((model[3].numchildren == 0) ? 0 : (1u << 19));
++  errorFlags |= ((model[3].children == NULL) ? 0 : (1u << 20));
++  errorFlags |= ((xcstrcmp(model[3].name, XCS("bar")) == 0) ? 0 : (1u << 21));
++
++  errorFlags |= ((model[4].type == XML_CTYPE_NAME) ? 0 : (1u << 22));
++  errorFlags |= ((model[4].quant == XML_CQUANT_NONE) ? 0 : (1u << 23));
++  errorFlags |= ((model[4].numchildren == 0) ? 0 : (1u << 24));
++  errorFlags |= ((model[4].children == NULL) ? 0 : (1u << 25));
++  errorFlags |= ((xcstrcmp(model[4].name, XCS("foo")) == 0) ? 0 : (1u << 26));
++
++  errorFlags |= ((model[5].type == XML_CTYPE_NAME) ? 0 : (1u << 27));
++  errorFlags |= ((model[5].quant == XML_CQUANT_PLUS) ? 0 : (1u << 28));
++  errorFlags |= ((model[5].numchildren == 0) ? 0 : (1u << 29));
++  errorFlags |= ((model[5].children == NULL) ? 0 : (1u << 30));
++  errorFlags |= ((xcstrcmp(model[5].name, XCS("xyz")) == 0) ? 0 : (1u << 31));
++
++  XML_SetUserData(parser, (void *)(uintptr_t)errorFlags);
++  XML_FreeContentModel(parser, model);
++}
++
++START_TEST(test_dtd_elements_nesting) {
++  // Payload inspired by a test in Perl's XML::Parser
++  const char *text = "<!DOCTYPE foo [\n"
++                     "<!ELEMENT junk ((bar|foo|xyz+), zebra*)>\n"
++                     "]>\n"
++                     "<foo/>";
++
++  XML_SetUserData(parser, (void *)(uintptr_t)-1);
++
++  XML_SetElementDeclHandler(parser, element_decl_check_model);
++  if (XML_Parse(parser, text, (int)strlen(text), XML_TRUE)
++      == XML_STATUS_ERROR)
++    xml_failure(parser);
++
++  if ((uint32_t)(uintptr_t)XML_GetUserData(parser) != 0)
++    fail("Element declaration model regression detected");
++}
++END_TEST
++
+ /* Test foreign DTD handling */
+ START_TEST(test_set_foreign_dtd)
+ {
+@@ -12256,6 +12331,7 @@ make_suite(void)
+     tcase_add_test(tc_basic, test_memory_allocation);
+     tcase_add_test(tc_basic, test_default_current);
+     tcase_add_test(tc_basic, test_dtd_elements);
++    tcase_add_test(tc_basic, test_dtd_elements_nesting);
+     tcase_add_test(tc_basic, test_set_foreign_dtd);
+     tcase_add_test(tc_basic, test_foreign_dtd_not_standalone);
+     tcase_add_test(tc_basic, test_invalid_foreign_dtd);
diff --git a/SOURCES/expat-2.2.5-Protect-against-malicious-namespace-declarations.patch b/SOURCES/expat-2.2.5-Protect-against-malicious-namespace-declarations.patch
new file mode 100644
index 0000000..acd46fb
--- /dev/null
+++ b/SOURCES/expat-2.2.5-Protect-against-malicious-namespace-declarations.patch
@@ -0,0 +1,229 @@
+commit fd5473ef5873048eadef344a1f16f71ad8eefe99
+Author: Tomas Korbar <tkorbar@redhat.com>
+Date:   Mon Mar 14 12:17:41 2022 +0100
+
+    Protect against malicious namespace declarations
+
+diff --git a/lib/xmlparse.c b/lib/xmlparse.c
+index 581b9a4..6f3510b 100644
+--- a/lib/xmlparse.c
++++ b/lib/xmlparse.c
+@@ -661,8 +661,7 @@ XML_ParserCreate(const XML_Char *encodingName)
+ XML_Parser XMLCALL
+ XML_ParserCreateNS(const XML_Char *encodingName, XML_Char nsSep)
+ {
+-  XML_Char tmp[2];
+-  *tmp = nsSep;
++  XML_Char tmp[2] = {nsSep, 0};
+   return XML_ParserCreate_MM(encodingName, NULL, tmp);
+ }
+ 
+@@ -1288,8 +1287,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser,
+      would be otherwise.
+   */
+   if (parser->m_ns) {
+-    XML_Char tmp[2];
+-    *tmp = parser->m_namespaceSeparator;
++    XML_Char tmp[2] = {parser->m_namespaceSeparator, 0};
+     parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd);
+   }
+   else {
+@@ -3640,6 +3638,117 @@ storeAtts(XML_Parser parser, const ENCODING *enc,
+   return XML_ERROR_NONE;
+ }
+ 
++static XML_Bool
++is_rfc3986_uri_char(XML_Char candidate) {
++  // For the RFC 3986 ANBF grammar see
++  // https://datatracker.ietf.org/doc/html/rfc3986#appendix-A
++
++  switch (candidate) {
++  // From rule "ALPHA" (uppercase half)
++  case 'A':
++  case 'B':
++  case 'C':
++  case 'D':
++  case 'E':
++  case 'F':
++  case 'G':
++  case 'H':
++  case 'I':
++  case 'J':
++  case 'K':
++  case 'L':
++  case 'M':
++  case 'N':
++  case 'O':
++  case 'P':
++  case 'Q':
++  case 'R':
++  case 'S':
++  case 'T':
++  case 'U':
++  case 'V':
++  case 'W':
++  case 'X':
++  case 'Y':
++  case 'Z':
++
++  // From rule "ALPHA" (lowercase half)
++  case 'a':
++  case 'b':
++  case 'c':
++  case 'd':
++  case 'e':
++  case 'f':
++  case 'g':
++  case 'h':
++  case 'i':
++  case 'j':
++  case 'k':
++  case 'l':
++  case 'm':
++  case 'n':
++  case 'o':
++  case 'p':
++  case 'q':
++  case 'r':
++  case 's':
++  case 't':
++  case 'u':
++  case 'v':
++  case 'w':
++  case 'x':
++  case 'y':
++  case 'z':
++
++  // From rule "DIGIT"
++  case '0':
++  case '1':
++  case '2':
++  case '3':
++  case '4':
++  case '5':
++  case '6':
++  case '7':
++  case '8':
++  case '9':
++
++  // From rule "pct-encoded"
++  case '%':
++
++  // From rule "unreserved"
++  case '-':
++  case '.':
++  case '_':
++  case '~':
++
++  // From rule "gen-delims"
++  case ':':
++  case '/':
++  case '?':
++  case '#':
++  case '[':
++  case ']':
++  case '@':
++
++  // From rule "sub-delims"
++  case '!':
++  case '$':
++  case '&':
++  case '\'':
++  case '(':
++  case ')':
++  case '*':
++  case '+':
++  case ',':
++  case ';':
++  case '=':
++    return XML_TRUE;
++
++  default:
++    return XML_FALSE;
++  }
++}
++
+ /* addBinding() overwrites the value of prefix->binding without checking.
+    Therefore one must keep track of the old value outside of addBinding().
+ */
+@@ -3700,6 +3809,29 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
+     if (!mustBeXML && isXMLNS
+         && (len > xmlnsLen || uri[len] != xmlnsNamespace[len]))
+       isXMLNS = XML_FALSE;
++
++    // NOTE: While Expat does not validate namespace URIs against RFC 3986
++    //       today (and is not REQUIRED to do so with regard to the XML 1.0
++    //       namespaces specification) we have to at least make sure, that
++    //       the application on top of Expat (that is likely splitting expanded
++    //       element names ("qualified names") of form
++    //       "[uri sep] local [sep prefix] '\0'" back into 1, 2 or 3 pieces
++    //       in its element handler code) cannot be confused by an attacker
++    //       putting additional namespace separator characters into namespace
++    //       declarations.  That would be ambiguous and not to be expected.
++    //
++    //       While the HTML API docs of function XML_ParserCreateNS have been
++    //       advising against use of a namespace separator character that can
++    //       appear in a URI for >20 years now, some widespread applications
++    //       are using URI characters (':' (colon) in particular) for a
++    //       namespace separator, in practice.  To keep these applications
++    //       functional, we only reject namespaces URIs containing the
++    //       application-chosen namespace separator if the chosen separator
++    //       is a non-URI character with regard to RFC 3986.
++    if (parser->m_ns && (uri[len] == parser->m_namespaceSeparator)
++        && ! is_rfc3986_uri_char(uri[len])) {
++      return XML_ERROR_SYNTAX;
++    }
+   }
+   isXML = isXML && len == xmlLen;
+   isXMLNS = isXMLNS && len == xmlnsLen;
+diff --git a/tests/runtests.c b/tests/runtests.c
+index ecc6f47..eabd55d 100644
+--- a/tests/runtests.c
++++ b/tests/runtests.c
+@@ -7950,6 +7950,38 @@ START_TEST(test_ns_double_colon_doctype)
+ }
+ 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, "<doc xmlns='one_two' />", XCS('\n')},
++      {XML_STATUS_ERROR, "<doc xmlns='one&#x0A;two' />", XCS('\n')},
++      {XML_STATUS_OK, "<doc xmlns='one:two' />", XCS(':')},
++  };
++
++  size_t i = 0;
++  size_t failCount = 0;
++  for (; i < sizeof(cases) / sizeof(cases[0]); i++) {
++    XML_Parser parser = XML_ParserCreateNS(NULL, cases[i].namesep);
++    XML_SetElementHandler(parser, dummy_start_element, dummy_end_element);
++    if (XML_Parse(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
++
++
+ /* Control variable; the number of times duff_allocator() will successfully allocate */
+ #define ALLOC_ALWAYS_SUCCEED (-1)
+ #define REALLOC_ALWAYS_SUCCEED (-1)
+@@ -12290,6 +12322,7 @@ make_suite(void)
+     tcase_add_test(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);
+ 
+     suite_add_tcase(s, tc_misc);
+     tcase_add_checked_fixture(tc_misc, NULL, basic_teardown);
diff --git a/SPECS/expat.spec b/SPECS/expat.spec
index df700a3..a0c8a18 100644
--- a/SPECS/expat.spec
+++ b/SPECS/expat.spec
@@ -3,7 +3,7 @@
 Summary: An XML parser library
 Name: expat
 Version: %(echo %{unversion} | sed 's/_/./g')
-Release: 5%{?dist}
+Release: 9%{?dist}
 Source: https://github.com/libexpat/libexpat/archive/R_%{unversion}.tar.gz#/expat-%{version}.tar.gz
 URL: https://libexpat.github.io/
 License: MIT
@@ -15,6 +15,11 @@ Patch3:	expat-2.2.5-Detect-and-prevent-integer-overflow-in-XML_GetBuffer.patch
 Patch4:	expat-2.2.5-Detect-and-prevent-troublesome-left-shifts.patch
 Patch5:	expat-2.2.5-Prevent-integer-overflow-on-m_groupSize-in-function.patch
 Patch6:	expat-2.2.5-Prevent-more-integer-overflows.patch
+Patch7: expat-2.2.5-Protect-against-malicious-namespace-declarations.patch
+Patch8: expat-2.2.5-Add-missing-validation-of-encoding.patch
+Patch9: expat-2.2.5-Prevent-integer-overflow-in-storeRawNames.patch
+Patch10: expat-2.2.5-Prevent-integer-overflow-in-copyString.patch
+Patch11: expat-2.2.5-Prevent-stack-exhaustion-in-build_model.patch
 
 %description
 This is expat, the C library for parsing XML, written by James Clark. Expat
@@ -49,6 +54,11 @@ Install it if you need to link statically with expat.
 %patch4 -p1 -b .CVE-2021-45960
 %patch5 -p1 -b .CVE-2021-46143
 %patch6 -p1 -b .CVE-2022-22822-CVE-2022-22827
+%patch7 -p1 -b .CVE-2022-25236
+%patch8 -p1 -b .CVE-2022-25235
+%patch9 -p1 -b .CVE-2022-25315
+%patch10 -p1 -b .CVE-2022-25314
+%patch11 -p1 -b .CVE-2022-25313
 
 sed -i 's/install-data-hook/do-nothing-please/' lib/Makefile.am
 ./buildconf.sh
@@ -87,6 +97,28 @@ make check
 %{_libdir}/lib*.a
 
 %changelog
+* Fri May 06 2022 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-9
+- Fix multiple CVEs
+- Resolves: CVE-2022-25314
+- Resolves: CVE-2022-25313
+
+* Mon Mar 14 2022 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-8
+- Improve patch for CVE-2022-25236
+- Related: CVE-2022-25236
+
+* Fri Mar 04 2022 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-7
+- Fix patch for CVE-2022-25235
+- Resolves: CVE-2022-25235
+
+* Thu Mar 03 2022 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-6
+- Fix multiple CVEs
+- CVE-2022-25236 expat: namespace-separator characters in "xmlns[:prefix]" attribute values can lead to arbitrary code execution
+- CVE-2022-25235 expat: malformed 2- and 3-byte UTF-8 sequences can lead to arbitrary code execution
+- CVE-2022-25315 expat: integer overflow in storeRawNames()
+- Resolves: CVE-2022-25236
+- Resolves: CVE-2022-25235
+- Resolves: CVE-2022-25315
+
 * Fri Feb 14 2022 Tomas Korbar <tkorbar@redhat.com> -  2.2.5-5
 - Fix multiple CVEs
 - CVE-2022-23852 expat: integer overflow in function XML_GetBuffer