fix(xml): harden XsdPreprocessor against XSD-schema XXE / entity-expansion - #261
Draft
ottobolyos wants to merge 1 commit into
Draft
fix(xml): harden XsdPreprocessor against XSD-schema XXE / entity-expansion#261ottobolyos wants to merge 1 commit into
ottobolyos wants to merge 1 commit into
Conversation
ottobolyos
force-pushed
the
fix/xml-xsd-preprocessor-xxe-hardening
branch
from
August 26, 2026 04:55
89252ba to
784a300
Compare
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 26, 2026
…integration/up-to-pr-261 Cascade rebuild after upstream/master advanced to 0ddc3c6 (PR TrakHound#220 merge).
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 26, 2026
…#261's new test file integration-branch-only glue fix, same class as the TrakHound#239/TrakHound#241/TrakHound#249 fixes already on this branch. TrakHound#261 builds clean standalone against current upstream/master without NUnit 4 present.
ottobolyos
force-pushed
the
fix/xml-xsd-preprocessor-xxe-hardening
branch
from
August 26, 2026 16:24
784a300 to
c2d7ea5
Compare
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 26, 2026
…integration/up-to-pr-261 on top of integration/up-to-pr-249
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 26, 2026
…integration/up-to-pr-261 on top of previous cascade tip
XsdPreprocessor.StripXsd11Constructs previously loaded untrusted XSD text through XDocument.Load with the default XmlReaderSettings. Those defaults leave the DocumentDoS-shape limits unbounded (MaxCharactersInDocument = 0, MaxCharactersFromEntities = 0 both mean "no limit"), making the preprocessor a viable target for the classic billion-laughs / quadratic-blowup XML entity-expansion attacks against an operator who points the tooling at an untrusted XSD URL. Route the load through an explicitly-tightened XmlReader: - DtdProcessing = Prohibit — the parser refuses any DOCTYPE declaration; entity expansion is impossible. - XmlResolver = null — external references are never resolved even if a DTD slipped through some future path. - MaxCharactersInDocument = 10,000,000 — bounded parser memory. - MaxCharactersFromEntities = 0 — no expansion capacity even in the DTD-permitted future. Also add a pre-parse size gate (MaxSourceCharacters = 10 MB) that fails loud with XmlException before the parser allocates. Silently returning the raw source on an oversized payload would hand it down to XmlSchema.Read, which lacks a matching cap. Tests: XsdPreprocessorSecurityTests pins four cases — oversized source raises XmlException; external DTD reference lands in the parse-error path; internal DTD entity expansion (billion-laughs shape) lands in the same path; a well-formed small XSD still round-trips through the hardened loader (happy-path regression guard).
ottobolyos
force-pushed
the
fix/xml-xsd-preprocessor-xxe-hardening
branch
from
August 27, 2026 02:49
c2d7ea5 to
99d9cc5
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Hardens
MTConnect.NET-XML/XsdPreprocessor.StripXsd11Constructsagainst XML-entity-expansion (billion-laughs / quadratic-blowup) attacks against operators who point the tooling at an untrusted XSD URL.Root Cause
Introduced in commit
13c19839(May 2026) — the originalStripXsd11Constructsimplementation loaded untrusted XSD text throughXDocument.Load(reader, LoadOptions.PreserveWhitespace)with the defaultXmlReaderSettings. Those defaults leave DoS-shape limits unbounded (MaxCharactersInDocument = 0,MaxCharactersFromEntities = 0both mean "no limit"), making the preprocessor a viable target for classic XML entity-expansion attacks.Fix
Route the load through an explicitly-tightened
XmlReader:DtdProcessing = Prohibit— the parser refuses anyDOCTYPEdeclaration; entity expansion is impossible.XmlResolver = null— external references are never resolved even if a DTD slipped through some future path.MaxCharactersInDocument = 10_000_000— bounded parser memory. Sized ~6× larger than the largest current MTConnect XSD schema in the repo (MTConnectStreams_2.7.xsdat 1.7 MB) with generous headroom for schema growth.MaxCharactersFromEntities = 0— no expansion capacity even in the DTD-permitted path.Scope
Single file (
libraries/MTConnect.NET-XML/XsdPreprocessor.cs) + new dedicated test file. No public API surface change — the hardening is transparent to callers that pass well-formed, bounded XSD input.Depends on
None. Independent of every other in-flight PR.