1 //------------------------------------------------------------------------------
2 // <copyright file="XmlUtils.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
7 namespace System.Web.Util {
11 using System.Xml.XPath;
13 using System.Diagnostics.CodeAnalysis;
15 internal static class XmlUtils
17 public static readonly long MaxEntityExpansion = 1024 * 1024;
19 [SuppressMessage("Microsoft.Security", "MSEC1208:DoNotUseLoadXml", Justification = "Handles developer-controlled input xml. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
20 [SuppressMessage("Microsoft.Security.Xml", "CA3057:DoNotUseLoadXml", Justification = "Handles developer-controlled input xml. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
21 public static XmlDocument CreateXmlDocumentFromContent(string content)
23 XmlDocument doc = new XmlDocument();
25 if (AppSettings.RestrictXmlControls) {
26 // We can't use the simple XmlDocument.LoadXml(string) here because there is no way to control the
27 // resolver used in that process. The only way we can do that is if we pass in our own XmlReader.
28 using (StringReader sreader = new StringReader(content)) {
29 doc.Load(CreateXmlReader(sreader));
38 [SuppressMessage("Microsoft.Security", "MSEC1210:UseXmlReaderForXPathDocument", Justification = "Handles developer-controlled input xml. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
39 [SuppressMessage("Microsoft.Security.Xml", "CA3059:UseXmlReaderForXPathDocument", Justification = "Handles developer-controlled input xml. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
40 public static XPathDocument CreateXPathDocumentFromContent(string content)
42 StringReader reader = new StringReader(content);
43 if (AppSettings.RestrictXmlControls) {
44 return new XPathDocument(CreateXmlReader(reader));
47 return new XPathDocument(reader);
51 [SuppressMessage("Microsoft.Security", "MSEC1220:ReviewDtdProcessingAssignment", Justification = "Dtd processing is needed for back-compat, but is being done as safely as possible.")]
52 [SuppressMessage("Microsoft.Security.Xml", "CA3069:ReviewDtdProcessingAssignment", Justification = "Dtd processing is needed for back-compat, but is being done as safely as possible.")]
53 public static XmlReaderSettings CreateXmlReaderSettings()
55 XmlReaderSettings settings = new XmlReaderSettings();
56 if (AppSettings.RestrictXmlControls)
58 settings.MaxCharactersFromEntities = XmlUtils.MaxEntityExpansion;
59 settings.XmlResolver = null;
60 // Prohibit is the default here. We don't need to prohibit DTD's, or even ignore them if we're using
61 // RestrictXmlControls, because we use a null resolver and limit/disable entity expansion.
62 settings.DtdProcessing = DtdProcessing.Parse;
67 // Ideally, these XmlReader factories would use XmlReader.Create() in the non-RestrictXmlControls case,
68 // but the default settings on that method are different from the default settings generated by XmlTextReader
69 // constructors, which is the code we're replacing with these factories. Since we want to keep doing
70 // whatever it was that we did before in this case, we'll just new up an XmlTextReader rather than
71 // try to guess at how to set matching defaults with XmlReader.Create().
72 // (E.g. DtdProcessing is Parse by default using XmlTextReader directly. It's Prohibit in default XmlReaderSettings.)
73 [SuppressMessage("Microsoft.Security", "MSEC1205:DoNotAllowDtdOnXmlTextReader", Justification = "Handles trusted or developer-controlled input xml. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
74 [SuppressMessage("Microsoft.Security.Xml", "CA3054:DoNotAllowDtdOnXmlTextReader", Justification = "Handles trusted or developer-controlled input xml. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
75 [SuppressMessage("Microsoft.Security", "MSEC1225:ReviewClassesDerivedFromXmlTextReader", Justification = "NoEntitiesXmlReader is our internal mechanism for using XmlTextReaders in a reasonably safe manner.")]
76 [SuppressMessage("Microsoft.Security.Xml", "CA3074:ReviewClassesDerivedFromXmlTextReader", Justification = "NoEntitiesXmlReader is our internal mechanism for using XmlTextReaders in a reasonably safe manner.")]
77 public static XmlReader CreateXmlReader(string filepath)
79 if (AppSettings.RestrictXmlControls)
81 NoEntitiesXmlTextReader nextr = new NoEntitiesXmlTextReader(filepath);
82 return XmlReader.Create(nextr, CreateXmlReaderSettings());
86 XmlTextReader xtr = new XmlTextReader(filepath);
91 [SuppressMessage("Microsoft.Security", "MSEC1205:DoNotAllowDtdOnXmlTextReader", Justification = "Handles trusted or developer-controlled input xml. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
92 [SuppressMessage("Microsoft.Security.Xml", "CA3054:DoNotAllowDtdOnXmlTextReader", Justification = "Handles trusted or developer-controlled input xml. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
93 [SuppressMessage("Microsoft.Security", "MSEC1225:ReviewClassesDerivedFromXmlTextReader", Justification = "NoEntitiesXmlReader is our internal mechanism for using XmlTextReaders in a reasonably safe manner.")]
94 [SuppressMessage("Microsoft.Security.Xml", "CA3074:ReviewClassesDerivedFromXmlTextReader", Justification = "NoEntitiesXmlReader is our internal mechanism for using XmlTextReaders in a reasonably safe manner.")]
95 public static XmlReader CreateXmlReader(Stream datastream)
97 if (AppSettings.RestrictXmlControls)
99 NoEntitiesXmlTextReader nextr = new NoEntitiesXmlTextReader(datastream);
100 return XmlReader.Create(nextr, CreateXmlReaderSettings());
104 XmlTextReader xtr = new XmlTextReader(datastream);
109 [SuppressMessage("Microsoft.Security", "MSEC1205:DoNotAllowDtdOnXmlTextReader", Justification = "Handles trusted or developer-controlled input xml. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
110 [SuppressMessage("Microsoft.Security.Xml", "CA3054:DoNotAllowDtdOnXmlTextReader", Justification = "Handles trusted or developer-controlled input xml. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
111 [SuppressMessage("Microsoft.Security", "MSEC1225:ReviewClassesDerivedFromXmlTextReader", Justification = "NoEntitiesXmlReader is our internal mechanism for using XmlTextReaders in a reasonably safe manner.")]
112 [SuppressMessage("Microsoft.Security.Xml", "CA3074:ReviewClassesDerivedFromXmlTextReader", Justification = "NoEntitiesXmlReader is our internal mechanism for using XmlTextReaders in a reasonably safe manner.")]
113 public static XmlReader CreateXmlReader(TextReader reader)
115 if (AppSettings.RestrictXmlControls)
117 NoEntitiesXmlTextReader nextr = new NoEntitiesXmlTextReader(reader);
118 return XmlReader.Create(nextr, CreateXmlReaderSettings());
122 XmlTextReader xtr = new XmlTextReader(reader);
127 [SuppressMessage("Microsoft.Security", "MSEC1205:DoNotAllowDtdOnXmlTextReader", Justification = "Handles trusted or developer-controlled input xml. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
128 [SuppressMessage("Microsoft.Security.Xml", "CA3054:DoNotAllowDtdOnXmlTextReader", Justification = "Handles trusted or developer-controlled input xml. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
129 [SuppressMessage("Microsoft.Security", "MSEC1225:ReviewClassesDerivedFromXmlTextReader", Justification = "NoEntitiesXmlReader is our internal mechanism for using XmlTextReaders in a reasonably safe manner.")]
130 [SuppressMessage("Microsoft.Security.Xml", "CA3074:ReviewClassesDerivedFromXmlTextReader", Justification = "NoEntitiesXmlReader is our internal mechanism for using XmlTextReaders in a reasonably safe manner.")]
131 public static XmlReader CreateXmlReader(Stream contentStream, string baseURI)
133 if (AppSettings.RestrictXmlControls)
135 NoEntitiesXmlTextReader nextr = new NoEntitiesXmlTextReader(baseURI, contentStream);
136 return XmlReader.Create(nextr, CreateXmlReaderSettings());
140 XmlTextReader xtr = new XmlTextReader(baseURI, contentStream);
145 // If you use any of these overloads that take in XmlReaderSettings, the suggestion is to get your base settings from
146 // CreateXmlReaderSettings(). That way you will have the correct defaults for RestrictXmlControls if applicable.
147 // Then you need to be smart about which settings you change before passing in here, because we will not
148 // re-enforce the correct settings, just in case you intentionally meant to change them.
149 public static XmlReader CreateXmlReader(TextReader reader, string baseURI, XmlReaderSettings settings)
151 if (settings == null) {
152 settings = CreateXmlReaderSettings();
155 // Note: If there is nothing materially changed in the settings, then Create() will just return your reader back
156 // to you and reader.Settings might still be null.
157 return XmlReader.Create(reader, settings, baseURI);
160 public static XslCompiledTransform CreateXslCompiledTransform(XmlReader xmlReader)
162 XmlReader readerToUse = xmlReader;
164 // XslCompiledTransform reconstructs its own XmlReader from scratch, so we can't rely entirely on the fancy
165 // protections we have in place on our readers. We need to bring out a bigger hammer and disable DTD's
166 // alltogether. We know how to work with XmlTextReader and which of its settings XslCompiledTransform will
167 // respect. For other reader types, just try to wrap it with new settings.
168 if (AppSettings.RestrictXmlControls) {
169 XmlTextReader xtr = xmlReader as XmlTextReader;
171 xtr.DtdProcessing = DtdProcessing.Ignore;
174 XmlReaderSettings settings = xmlReader.Settings;
175 if (settings == null) {
176 settings = CreateXmlReaderSettings();
178 settings.DtdProcessing = DtdProcessing.Ignore;
179 readerToUse = XmlReader.Create(xmlReader, settings);
183 XslCompiledTransform compiledTransform = new XslCompiledTransform();
184 // The second parameter is XsltSettings. null results in the XsltSettings.Default being used, which disables the document function, and script
185 // The third parameter is an XmlResolver to be used
186 compiledTransform.Load(readerToUse, null, null);
187 return compiledTransform;
191 #pragma warning disable 0618 // To avoid deprecation warning
192 [SuppressMessage("Microsoft.Security", "MSEC1201:DoNotUseXslTransform", Justification = "Handles developer-controlled input xsl. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
193 [SuppressMessage("Microsoft.Security.Xml", "CA3050:DoNotUseXslTransform", Justification = "Handles developer-controlled input xsl. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
194 public static XslTransform CreateXslTransform(XmlReader reader)
196 if (!AppSettings.RestrictXmlControls)
198 XslTransform xform = new XslTransform();
205 [SuppressMessage("Microsoft.Security", "MSEC1201:DoNotUseXslTransform", Justification = "Handles developer-controlled input xsl. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
206 [SuppressMessage("Microsoft.Security.Xml", "CA3050:DoNotUseXslTransform", Justification = "Handles developer-controlled input xsl. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
207 public static XslTransform CreateXslTransform(XmlReader reader, XmlResolver resolver)
209 if (!AppSettings.RestrictXmlControls)
211 XslTransform xform = new XslTransform();
212 xform.Load(reader, resolver, null);
218 public static XslTransform GetXslTransform(XslTransform xform)
220 return (AppSettings.RestrictXmlControls ? null : xform);
222 #pragma warning restore 0618
224 // This class exists to override the ResolveEntity() method, which can be used to force resolution of custom/external
225 // entities in Xml files. When we use this class, we should have already set EntityHandling to EntityHandling.ExpandCharEntities,
226 // which will disable custom/external entity expansion by default. But this extra protection will keep people from unintentionally
227 // shooting themselves in the foot when they think they might have been safe.
228 private sealed class NoEntitiesXmlTextReader : XmlTextReader
230 [SuppressMessage("Microsoft.Security", "MSEC1205:DoNotAllowDtdOnXmlTextReader", Justification = "Handles developer-controlled input xml. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
231 public NoEntitiesXmlTextReader() : base() { Restrict(); }
232 [SuppressMessage("Microsoft.Security", "MSEC1205:DoNotAllowDtdOnXmlTextReader", Justification = "Handles developer-controlled input xml. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
233 public NoEntitiesXmlTextReader(string filepath) : base(filepath) { Restrict(); }
234 [SuppressMessage("Microsoft.Security", "MSEC1205:DoNotAllowDtdOnXmlTextReader", Justification = "Handles developer-controlled input xml. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
235 public NoEntitiesXmlTextReader(TextReader reader) : base(reader) { Restrict(); }
236 [SuppressMessage("Microsoft.Security", "MSEC1205:DoNotAllowDtdOnXmlTextReader", Justification = "Handles developer-controlled input xml. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
237 public NoEntitiesXmlTextReader(Stream datastream) : base(datastream) { Restrict(); }
238 [SuppressMessage("Microsoft.Security", "MSEC1205:DoNotAllowDtdOnXmlTextReader", Justification = "Handles developer-controlled input xml. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
239 public NoEntitiesXmlTextReader(string baseURI, Stream contentStream) : base(baseURI, contentStream) { Restrict(); }
241 public override void ResolveEntity()
243 // Do not ever do general entity expansion/replacement, even when asked
247 private void Restrict() {
248 EntityHandling = EntityHandling.ExpandCharEntities;