Affected versions

Symfony Config versions <5.4.56, >=6, <6.4.50, >=7, <7.4.23, >=8, <8.1.11 are affected by this security issue.

The issue has been fixed in Symfony Config 5.4.56, 6.4.50, 7.4.23, 8.1.11.

Description

Symfony\Component\Config\Util\XmlUtils::parse() sets $dom->validateOnParse = true immediately before calling loadXML(), and only rejects documents carrying a doctype afterwards. Enabling DTD validation makes libxml load and process the doctype during loadXML(), which includes resolving external entities. LIBXML_NONET blocks http:// but not file://, so a file:// entity is read from disk before the doctype rejection can run.

This is the same defect as CVE-2026-45071 (GHSA-x6g4-fwcc-jj8w), which removed the flag from Crawler::addXmlContent() in the DomCrawler component. The occurrence in the Config component was not addressed at the time.

The clearest evidence is what libxml reports when the referenced file is not well formed on its own, since the errors are attributed to that file:

1
2
3
[ERROR 77] Premature end of data in tag root line 2 (in file:///.../invalid.xml - line 3, column 1)
[ERROR 85] chunk is not well balanced (in file:///.../invalid.xml - line 3, column 1)
[ERROR 26] Entity 'xxe' failed to parse

libxml can only produce those by opening and reading the file. Isolating the flag confirms it directly: with validateOnParse = true, an entity pointing at a readable file yields its content in the DOM, and one pointing at a missing path yields failed to load external entity "file:///...". With the flag off, neither happens.

Impact depends on the stream wrappers. The doctype check throws XmlParsingException with a fixed message, so the DOM never reaches the caller. But the external entities are resolved before that check. A parameter entity can read a local file through php://filter and put its content into the URL of a second external entity. LIBXML_NONET blocks http://, so PHP's built-in wrappers give that URL no way out. A network-capable stream wrapper registered by the application, such as s3://, does: the content of the file then leaves the server. On every setup, an attacker also gets a file-existence oracle from the distinct libxml error on a missing path, and a denial of service vector by pointing the entity at a large or slow file.

Reachability is limited. The callers of XmlUtils::parse() in Symfony itself (the XML loaders of DependencyInjection, Routing, Validator and Serializer, XliffFileLoader and QtFileLoader) consume developer-provided configuration and translation files, which are trusted input; the Loco, Crowdin and Lokalise translation providers feed it the XLIFF documents fetched from the operator-configured service. The exposure is applications that pass untrusted XML to XmlUtils::parse() directly, which its string-accepting signature permits. The severity is rated low on that basis, matching the rating of CVE-2026-45071.

Resolution

The $dom->validateOnParse = true; line is removed. Nothing else changes: doctype rejection, schema validation and error reporting are untouched.

The flag was dead weight independently of the security question. It can only act on a document carrying a doctype, which this method rejects unconditionally three lines later, so validation could never legitimately succeed. On every doctype-less document, meaning every valid Symfony configuration file, it merely produced a spurious Validation failed: no DTD found ! entry in the libxml error buffer. Schema validation is a separate concern handled through the $schemaOrCallable argument and never relied on this flag.

The patches for this issue are available here for branch 5.4 (and forward-ported to 6.4, 7.4, 8.1 and 8.2).

Credits

We would like to thank Prithiv Kumaravel (ProjectDiscovery) and Björn Meyer from Shopware AG for reporting the issue.