Merge pull request #2964 from ludovic-henry/sgen-monocontext
[mono.git] / mcs / class / referencesource / System.Web / Util / XmlUtils.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="XmlUtils.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 namespace System.Web.Util {
8
9     using System.IO;
10     using System.Xml;
11     using System.Xml.XPath;
12     using System.Xml.Xsl;
13     using System.Diagnostics.CodeAnalysis;
14
15     internal static class XmlUtils
16     {
17         public static readonly long MaxEntityExpansion = 1024 * 1024;
18
19         [SuppressMessage("Microsoft.Security", "MSEC1208:DoNotUseLoadXml", Justification = "Handles developer-controlled input xml.  Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
20         public static XmlDocument CreateXmlDocumentFromContent(string content)
21         {
22             XmlDocument doc = new XmlDocument();
23
24            if (AppSettings.RestrictXmlControls) {
25                 // We can't use the simple XmlDocument.LoadXml(string) here because there is no way to control the
26                 // resolver used in that process.  The only way we can do that is if we pass in our own XmlReader.
27                 using (StringReader sreader = new StringReader(content)) {
28                     doc.Load(CreateXmlReader(sreader));
29                 }
30             }
31             else {
32                 doc.LoadXml(content);
33             }
34             return doc;
35         }
36
37         [SuppressMessage("Microsoft.Security", "MSEC1210:UseXmlReaderForXPathDocument", Justification = "Handles developer-controlled input xml.  Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
38         public static XPathDocument CreateXPathDocumentFromContent(string content)
39         {
40             StringReader reader = new StringReader(content);
41             if (AppSettings.RestrictXmlControls) {
42                 return new XPathDocument(CreateXmlReader(reader));
43             }
44             else {
45                 return new XPathDocument(reader);
46             }
47         }
48
49         [SuppressMessage("Microsoft.Security", "MSEC1220:ReviewDtdProcessingAssignment", Justification = "Dtd processing is needed for back-compat, but is being done as safely as possible.")]
50         public static XmlReaderSettings CreateXmlReaderSettings()
51         {
52             XmlReaderSettings settings = new XmlReaderSettings();
53             if (AppSettings.RestrictXmlControls)
54             {
55                 settings.MaxCharactersFromEntities = XmlUtils.MaxEntityExpansion;
56                 settings.XmlResolver = null;
57                 // Prohibit is the default here.  We don't need to prohibit DTD's, or even ignore them if we're using
58                 // RestrictXmlControls, because we use a null resolver and limit/disable entity expansion.
59                 settings.DtdProcessing = DtdProcessing.Parse;
60             }
61             return settings;
62         }
63
64         // Ideally, these XmlReader factories would use XmlReader.Create() in the non-RestrictXmlControls case,
65         // but the default settings on that method are different from the default settings generated by XmlTextReader
66         // constructors, which is the code we're replacing with these factories.  Since we want to keep doing
67         // whatever it was that we did before in this case, we'll just new up an XmlTextReader rather than
68         // try to guess at how to set matching defaults with XmlReader.Create().
69         // (E.g. DtdProcessing is Parse by default using XmlTextReader directly.  It's Prohibit in default XmlReaderSettings.)
70         [SuppressMessage("Microsoft.Security", "MSEC1205:DoNotAllowDtdOnXmlTextReader", Justification = "Handles trusted or developer-controlled input xml.  Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
71         [SuppressMessage("Microsoft.Security", "MSEC1225:ReviewClassesDerivedFromXmlTextReader", Justification = "NoEntitiesXmlReader is our internal mechanism for using XmlTextReaders in a reasonably safe manner.")]
72         public static XmlReader CreateXmlReader(string filepath)
73         {
74             if (AppSettings.RestrictXmlControls)
75             {
76                 NoEntitiesXmlTextReader nextr = new NoEntitiesXmlTextReader(filepath);
77                 return XmlReader.Create(nextr, CreateXmlReaderSettings());
78             }
79             else
80             {
81                 XmlTextReader xtr = new XmlTextReader(filepath);
82                 return xtr;
83             }
84         }
85
86         [SuppressMessage("Microsoft.Security", "MSEC1205:DoNotAllowDtdOnXmlTextReader", Justification = "Handles trusted or developer-controlled input xml.  Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
87         [SuppressMessage("Microsoft.Security", "MSEC1225:ReviewClassesDerivedFromXmlTextReader", Justification = "NoEntitiesXmlReader is our internal mechanism for using XmlTextReaders in a reasonably safe manner.")]
88         public static XmlReader CreateXmlReader(Stream datastream)
89         {
90             if (AppSettings.RestrictXmlControls)
91             {
92                 NoEntitiesXmlTextReader nextr = new NoEntitiesXmlTextReader(datastream);
93                 return XmlReader.Create(nextr, CreateXmlReaderSettings());
94             }
95             else
96             {
97                 XmlTextReader xtr = new XmlTextReader(datastream);
98                 return xtr;
99             }
100         }
101
102         [SuppressMessage("Microsoft.Security", "MSEC1205:DoNotAllowDtdOnXmlTextReader", Justification = "Handles trusted or developer-controlled input xml.  Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
103         [SuppressMessage("Microsoft.Security", "MSEC1225:ReviewClassesDerivedFromXmlTextReader", Justification = "NoEntitiesXmlReader is our internal mechanism for using XmlTextReaders in a reasonably safe manner.")]
104         public static XmlReader CreateXmlReader(TextReader reader)
105         {
106             if (AppSettings.RestrictXmlControls)
107             {
108                 NoEntitiesXmlTextReader nextr = new NoEntitiesXmlTextReader(reader);
109                 return XmlReader.Create(nextr, CreateXmlReaderSettings());
110             }
111             else
112             {
113                 XmlTextReader xtr = new XmlTextReader(reader);
114                 return xtr;
115             }
116         }
117
118         [SuppressMessage("Microsoft.Security", "MSEC1205:DoNotAllowDtdOnXmlTextReader", Justification = "Handles trusted or developer-controlled input xml.  Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
119         [SuppressMessage("Microsoft.Security", "MSEC1225:ReviewClassesDerivedFromXmlTextReader", Justification = "NoEntitiesXmlReader is our internal mechanism for using XmlTextReaders in a reasonably safe manner.")]
120         public static XmlReader CreateXmlReader(Stream contentStream, string baseURI)
121         {
122             if (AppSettings.RestrictXmlControls)
123             {
124                 NoEntitiesXmlTextReader nextr = new NoEntitiesXmlTextReader(baseURI, contentStream);
125                 return XmlReader.Create(nextr, CreateXmlReaderSettings());
126             }
127             else
128             {
129                 XmlTextReader xtr = new XmlTextReader(baseURI, contentStream);
130                 return xtr;
131             }
132         }
133
134         // If you use any of these overloads that take in XmlReaderSettings, the suggestion is to get your base settings from
135         // CreateXmlReaderSettings().  That way you will have the correct defaults for RestrictXmlControls if applicable.
136         // Then you need to be smart about which settings you change before passing in here, because we will not
137         // re-enforce the correct settings, just in case you intentionally meant to change them.
138         public static XmlReader CreateXmlReader(TextReader reader, string baseURI, XmlReaderSettings settings)
139         {
140             if (settings == null) {
141                 settings = CreateXmlReaderSettings();
142             }
143
144             // Note:  If there is nothing materially changed in the settings, then Create() will just return your reader back
145             // to you and reader.Settings might still be null.
146             return XmlReader.Create(reader, settings, baseURI);
147         }
148
149         public static XslCompiledTransform CreateXslCompiledTransform(XmlReader xmlReader)
150         {
151             XmlReader readerToUse = xmlReader;
152
153             // XslCompiledTransform reconstructs its own XmlReader from scratch, so we can't rely entirely on the fancy
154             // protections we have in place on our readers.  We need to bring out a bigger hammer and disable DTD's
155             // alltogether.  We know how to work with XmlTextReader and which of its settings XslCompiledTransform will
156             // respect.  For other reader types, just try to wrap it with new settings.
157             if (AppSettings.RestrictXmlControls) {
158                 XmlTextReader xtr = xmlReader as XmlTextReader;
159                 if (xtr != null) {
160                     xtr.DtdProcessing = DtdProcessing.Ignore;
161                 }
162                 else {
163                     XmlReaderSettings settings = xmlReader.Settings;
164                     if (settings == null) {
165                         settings = CreateXmlReaderSettings();
166                     }
167                     settings.DtdProcessing = DtdProcessing.Ignore;
168                     readerToUse = XmlReader.Create(xmlReader, settings);
169                 }
170             }
171
172             XslCompiledTransform compiledTransform = new XslCompiledTransform();
173             // The second parameter is XsltSettings.  null results in the XsltSettings.Default being used, which disables the document function, and script
174             // The third parameter is an XmlResolver to be used
175             compiledTransform.Load(readerToUse, null, null);
176             return compiledTransform;
177         }
178
179
180 #pragma warning disable 0618    // To avoid deprecation warning
181         [SuppressMessage("Microsoft.Security", "MSEC1201:DoNotUseXslTransform", Justification = "Handles developer-controlled input xsl.  Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
182         public static XslTransform CreateXslTransform(XmlReader reader)
183         {
184             if (!AppSettings.RestrictXmlControls)
185             {
186                 XslTransform xform = new XslTransform();
187                 xform.Load(reader);
188                 return xform;
189             }
190             return null;
191         }
192
193         [SuppressMessage("Microsoft.Security", "MSEC1201:DoNotUseXslTransform", Justification = "Handles developer-controlled input xsl.  Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
194         public static XslTransform CreateXslTransform(XmlReader reader, XmlResolver resolver)
195         {
196             if (!AppSettings.RestrictXmlControls)
197             {
198                 XslTransform xform = new XslTransform();
199                 xform.Load(reader, resolver, null);
200                 return xform;
201             }
202             return null;
203         }
204
205         public static XslTransform GetXslTransform(XslTransform xform)
206         {
207             return (AppSettings.RestrictXmlControls ? null : xform);
208         }
209 #pragma warning restore 0618
210
211         // This class exists to override the ResolveEntity() method, which can be used to force resolution of custom/external
212         // entities in Xml files.  When we use this class, we should have already set EntityHandling to EntityHandling.ExpandCharEntities,
213         // which will disable custom/external entity expansion by default.  But this extra protection will keep people from unintentionally
214         // shooting themselves in the foot when they think they might have been safe.
215         private sealed class NoEntitiesXmlTextReader : XmlTextReader
216         {
217             [SuppressMessage("Microsoft.Security", "MSEC1205:DoNotAllowDtdOnXmlTextReader", Justification = "Handles developer-controlled input xml.  Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
218             public NoEntitiesXmlTextReader() : base() { Restrict(); }
219             [SuppressMessage("Microsoft.Security", "MSEC1205:DoNotAllowDtdOnXmlTextReader", Justification = "Handles developer-controlled input xml.  Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
220             public NoEntitiesXmlTextReader(string filepath) : base(filepath) { Restrict(); }
221             [SuppressMessage("Microsoft.Security", "MSEC1205:DoNotAllowDtdOnXmlTextReader", Justification = "Handles developer-controlled input xml.  Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
222             public NoEntitiesXmlTextReader(TextReader reader) : base(reader) { Restrict(); }
223             [SuppressMessage("Microsoft.Security", "MSEC1205:DoNotAllowDtdOnXmlTextReader", Justification = "Handles developer-controlled input xml.  Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
224             public NoEntitiesXmlTextReader(Stream datastream) : base(datastream) { Restrict(); }
225             [SuppressMessage("Microsoft.Security", "MSEC1205:DoNotAllowDtdOnXmlTextReader", Justification = "Handles developer-controlled input xml.  Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
226             public NoEntitiesXmlTextReader(string baseURI, Stream contentStream) : base(baseURI, contentStream) { Restrict(); }
227
228             public override void ResolveEntity()
229             {
230                 // Do not ever do general entity expansion/replacement, even when asked
231                 return;
232             }
233
234             private void Restrict() {
235                 EntityHandling = EntityHandling.ExpandCharEntities;
236                 XmlResolver = null;
237             }
238         }
239     }
240 }