Skip to content

Security: harden user-import XML parsing against XXE#8609

Merged
AngelFQC merged 2 commits into
masterfrom
security/auto-fix-GHSA-h24x-xw47-2wwx
Jun 17, 2026
Merged

Security: harden user-import XML parsing against XXE#8609
AngelFQC merged 2 commits into
masterfrom
security/auto-fix-GHSA-h24x-xw47-2wwx

Conversation

@AngelFQC

Copy link
Copy Markdown
Member

Problem

The user import and update features parse an admin-uploaded XML file through Crawler::addXmlContent(file_get_contents($file)) without any explicit XML hardening. Protection against external-entity resolution was left entirely to the libxml runtime default, so on PHP < 8 or a misconfigured/downgraded environment the parser could resolve external entities and disclose local files.

Fix

Wrap each user-import XML parse with an explicit, non-deprecated guard that blocks external entity loading regardless of the runtime default, then restores the default loader:

libxml_set_external_entity_loader(static fn () => null);
$crawler->addXmlContent(file_get_contents($file));
libxml_set_external_entity_loader(null);

Applied at all three user-import parse sites: myspace.lib.php::parse_xml_data(), admin/user_import.php and admin/user_update_import.php.

Notes:

  • libxml_disable_entity_loader() is deprecated on PHP 8.x, so the modern libxml_set_external_entity_loader() is used instead.
  • LIBXML_NOENT is intentionally not used — it would enable entity substitution and make the situation worse. Symfony's addXmlContent() keeps its safe LIBXML_NONET default.

Invariant now enforced

User-import XML parsing never resolves external entities (e.g. file:///…), independent of the PHP/libxml version or configuration.

⚠ Scope note

lp/scorm.class.php parses uploaded SCORM XML through the same addXmlContent pattern; it is a different feature outside this advisory's scope and was left untouched. It would benefit from the same hardening as a follow-up.

OWASP control

A05:2021 – Security Misconfiguration (CWE-611, XML External Entity).

Refs GHSA-h24x-xw47-2wwx

🤖 Generated with Claude Code

…2wwx)

Centralize XML parsing for the user-import flows in a single
Import::xml()/xmlFromString() helper that hardens against XXE by blocking
external entity loading via libxml_set_external_entity_loader, restoring the
default loader in a finally block.

This mirrors the 1.11.x Import::xml() approach but uses the modern API
(libxml_disable_entity_loader is deprecated and a no-op on PHP 8.x; LIBXML_NOENT
would make things worse by enabling entity expansion).

Refactors parse_xml_data in myspace.lib.php, user_import.php and
user_update_import.php to call Import::xml($file).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@AngelFQC AngelFQC force-pushed the security/auto-fix-GHSA-h24x-xw47-2wwx branch from 393590a to e38ea6e Compare June 17, 2026 22:36
Apply the same XXE hardening to the other XML import surfaces found while
auditing Crawler/loadXML usage:

- scorm.class.php (SCORM manifest) and exercise_import.inc.php (QTI2) now
  parse through the centralized Import::xmlFromString() helper instead of a
  bare Crawler->addXmlContent().
- Moodle backup and Common Cartridge importers (src/CourseBundle) now pass
  LIBXML_NONET to DOMDocument::loadXML(), matching the convention already
  used by the SVG and legacy Moodle parsers.

PDF (HTML) and the LIBXML_NONET-protected simplexml parsers were reviewed
and are not affected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@AngelFQC AngelFQC merged commit 95dcc4d into master Jun 17, 2026
2 of 12 checks passed
@AngelFQC AngelFQC deleted the security/auto-fix-GHSA-h24x-xw47-2wwx branch June 17, 2026 23:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant