using System; using System.IO; using System.Text; using System.Xml; using System.Xml.Serialization; namespace Wayne.Lib { /// <summary> /// A util class to create <see cref="XmlDocument"/> and <see cref="XmlTextReader"/> classes resistant to XXE attacks.> /// </summary> /// <seealso> /// <cref>https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet</cref> /// </seealso> public static class Xml { #region XmlDocument Constructors /// <summary> /// Initializes a new instance of the <see cref="XmlDocument"/> class. /// </summary> public static XmlDocument Document() { return FixDocument(new XmlDocument()); } /// <summary> /// Initializes a new instance of the <see cref="XmlDocument"/> class with the specified <see cref="XmlNameTable"/>. /// </summary> public static XmlDocument Document(XmlNameTable nt) { return FixDocument(new XmlDocument(nt)); } #endregion #region XmlReader Constructors /// <summary> /// Replace <see cref="XmlReader"/> Create method. Creates a new <see cref="XmlReader"/> instance using the specified <see cref="Stream"/> with default settings. /// </summary> public static XmlReader Reader(Stream input) { return XmlReader.Create(input, FixSettings()); } /// <summary> /// Replace <see cref="XmlReader"/> Create method. Creates a new <see cref="XmlReader"/> instance using the specified <see cref="Stream"/> and <see cref="XmlReaderSettings"/>. /// </summary> public static XmlReader Reader(Stream input, XmlReaderSettings settings) { return XmlReader.Create(input, FixSettings(settings)); } /// <summary> /// Replace <see cref="XmlReader"/> Create method. Creates a new <see cref="XmlReader"/> instance by using the specified <see cref="System.IO.TextReader"/>. /// </summary> public static XmlReader Reader(TextReader input) { return XmlReader.Create(input, FixSettings()); } /// <summary> /// Replace <see cref="XmlReader"/> Create method. Creates a new <see cref="XmlReader"/> instance by using the specified <see cref="System.IO.TextReader"/> and <see cref="XmlReaderSettings"/>. /// </summary> public static XmlReader Reader(TextReader input, XmlReaderSettings settings) { return XmlReader.Create(input, FixSettings(settings)); } #endregion #region GetEncoding /// <summary> /// Gets the encoded <see cref="string"/> of the undeling xml data. Uses <see cref="XmlTextReader"/> to read the data. /// </summary> public static string GetEncodedString(byte[] data, int offset, int length) { string ret = null; System.Text.Encoding encoding = GetEncoding(data, offset, length); if (encoding != null && encoding.CodePage != 0) { ret = encoding.GetString(data, offset, length); } else { ret = System.Text.Encoding.UTF8.GetString(data, offset, length); } return ret; } #endregion #region Serialization /// <summary> /// Serializes a class instance to <see cref="String"/>. /// </summary> public static string Serialize<T>(T instance) where T : class { var xmlWriterSettings = new XmlWriterSettings { Encoding = new UTF8Encoding(false) }; using (var stream = new MemoryStream()) using (var writer = XmlWriter.Create(stream, xmlWriterSettings)) { var serializer = new XmlSerializer(typeof(T)); serializer.Serialize(writer, instance); return System.Text.Encoding.UTF8.GetString(stream.ToArray(), 0, (int)stream.Length); } } /// <summary> /// Deserialize a xml <see cref="String"/>. /// </summary> public static T Deserialize<T>(string xml) where T : class { using (var reader = new StringReader(xml)) { var serializer = new XmlSerializer(typeof(T)); return serializer.Deserialize(reader) as T; } } /// <summary> /// Deserializes XmlDocument object to Serializable object of type T. /// </summary> /// <typeparam name="T">Serializable object type as output type.</typeparam> /// <param name="document">XmlDocument object to be deserialized.</param> /// <returns>Deserialized serializable object of type T.</returns> public static T Deserialize<T>(XmlDocument document) where T : class { using (var reader = new XmlNodeReader(document)) { var serializer = new XmlSerializer(typeof(T)); return serializer.Deserialize(reader) as T; } } #endregion #region Implementation private static System.Text.Encoding GetEncoding(byte[] data, int offset, int length) { System.Text.Encoding encoding = null; using (var stream = new MemoryStream(data, offset, length)) { using (var reader = FixTextReader(new XmlTextReader(stream))) { reader.Read(); encoding = reader.Encoding; } } return encoding; } private static XmlDocument FixDocument(XmlDocument document) { document.XmlResolver = null; // Setting XmlResolver to NULL disables DTDs - Its NOT null by default. return document; } private static XmlTextReader FixTextReader(XmlTextReader reader) { #if WindowsCE || OldFramework //Dtd processing is not supprted in .Net Compact Framework #else reader.DtdProcessing = DtdProcessing.Prohibit; // Needed because the default is Parse!! #endif return reader; } private static XmlReaderSettings FixSettings() { return FixSettings(null); } private static XmlReaderSettings FixSettings(XmlReaderSettings settings) { if (settings == null) { settings = new XmlReaderSettings(); } #if WindowsCE || OldFramework //Dtd processing is not supprted in .Net Compact Framework #else settings.DtdProcessing = DtdProcessing.Prohibit; // Needed because the default is Parse!! #endif return settings; } #endregion } }