Processing XML from non-standard DTD in C# - c#

I literally just want to be able to traverse the contents of various XML files that I have been given but having a non-standard DTD means I am hitting some issues - one error being "Reference to undeclared entity 'reg'" and another saying it is unable to locate the .DTD file.
Is it possible to do this sort of thing for XML files when no DTD is available? I have no control over these files and cannot change them. I am looking to grab various amounts of them at a time, move through the contents as efficiently as possible, email out some notifications and thats it.
Sample of XML file below:
<!DOCTYPE Toro-Pub PUBLIC "-//Toro//DTD Toro Publication V1.0//EN//XML" "Toro-Pub.dtd">
<!--Arbortext, Inc., 1988-2011, v.4002-->
<?Pub UDT _nopagebreak _touchup KeepsKeep="yes" KeepsPrev="no" KeepsNext="no" KeepsBoundary="page"?>
<?Pub UDT template _font?>
<?Pub UDT _bookmark _target?>
<?Pub UDT _nocolumnbreak _touchup KeepsKeep="yes" KeepsPrev="no" KeepsNext="no" KeepsBoundary="column"?>
<?Pub UDT instructions _comment FontColor="red"?>
<?Pub EntList alpha bull copy rArr sect trade deg?>
<?Pub Inc?>
<Toro-Pub><PubMeta Brand="Toro" CE="Yes" ClientPubNo="" CopyrightYear="2013" FormNumber="3378-827" Lang="CS" LangParentForm="3378-826" LangParentID="72729" LangParentRev="A" PageSize="" PhoneNoCan="" PhoneNoMex="" PhoneNoUS="" ProductFamily="sample product name" PubID="72730" PublicationType="Operator Manual" RegistrationURL="www.website.com" Rev="A" ServiceURL="www.website.com"><?TranslationData DueDate="07/01/2013" InCarton(1-yes)="0" Author="Mr Smith" EngParent="https://lwww.website.com?vPubID=423&vPubNum=3378-826" ?></PubMeta><Pub-TBlock>
<Body-TB>
...
Many thanks.
UPDATE #1
I have tried the below code taken from the suggested comment:
Stream file = File.OpenRead("4d00fa60800e0a5d_3378-827.xml");
// The next line is the fix!!!
XmlTextReader xmlTextReader = new XmlTextReader(file);
xmlTextReader.XmlResolver = null; // Don't require file in system32\inetsrv
XmlReaderSettings readerSettings = new XmlReaderSettings();
readerSettings.ValidationType = ValidationType.Schema;
//readerSettings.Schemas.Add(null, "");
readerSettings.DtdProcessing = DtdProcessing.Ignore;
readerSettings.XmlResolver = null; // Doesn't help
//readerSettings.ValidationEventHandler += ValidationEventHandle;
XmlReader myXmlReader = XmlReader.Create(xmlTextReader, readerSettings);
XmlDocument myXmlDocument = new XmlDocument();
myXmlDocument.XmlResolver = null; // Doesn't help
myXmlDocument.Load(myXmlReader); // Load doc, no .dtd required on local disk
However, I now get a new error of 'Operation is not valid due to the current state of the object.' on the line 'myXmlDocument.Load(myXmlReader)'.

Related

Minio. For security reasons DTD is prohibited in this XML document

Consider code that (tries) to put a file to Minio:
public async Task Put(byte[] data)
{
using var ms = new MemoryStream(data);
var args = new PutObjectArgs { };
args.WithBucket("buckethead");
args.WithObject(Guid.NewGuid.ToString());
args.WithStreamData(ms);
args.WithObjectSize(ms.Length);
args.WithContentType("application/vnd.ms-excel");
await _client.PutObjectAsync(args);
}
Data is a ClosedXML XLTemplate, saved as bytes:
var template = new XLTemplate(#"D:\Documents\MyTemplate.xlsx");
template.AddVariable(myDto); //just a dto class with values to fill a template
template.Generate();
using var ms = new MemoryStream();
template.SaveAs(ms);
return ms.ToArray();
Problems is, this line:
await _client.PutObjectAsync(args);
Fails with the following:
{"There is an error in XML document (0, 0)."}
For security reasons DTD is prohibited in this XML document. To enable DTD processing set the DtdProcessing property on XmlReaderSettings to Parse and pass the settings into XmlReader.Create method.
at System.Xml.XmlTextReaderImpl.Throw(Exception e)
at System.Xml.XmlTextReaderImpl.ParseDoctypeDecl()
at System.Xml.XmlTextReaderImpl.ParseDocumentContent()
at System.Xml.XmlTextReaderImpl.Read()
at System.Xml.XmlReader.MoveToContent()
at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderErrorResponse.Read3_Error()
What does it have to do with serialization and how to fix it?
Btw created XLTemplate is legit, if saved to hard drive as .xlsx I can open it just fine.
Turns out I had port specified incorrectly in Minio client. Well the exception never even hinted at it. Don't manage exception verbosity in your libraries like this, kids.

How to get only modified nodes using XmlDiffView GetHTML

We have two xml files and need to find the diff of it. For this we are using XMLDiff library. We are able to get the difference but now wanted to have a UI which shows modified nodes. So used XmlDiffView class. Code is as below
XmlDiffView dv = new XmlDiffView();
//Load the original file again and the diff file.
XmlTextReader orig = new XmlTextReader(oldXML);
XmlTextReader diffGram = new XmlTextReader(diffXML);
dv.Load(orig,
diffGram);
//Wrap the HTML file with necessary html and
//body tags and prepare it before passing it to the GetHtml method.
string tempFile = #"C:\Users\ABC\Desktop\diffView.html";
StreamWriter sw1 = new StreamWriter(tempFile);
sw1.Write("<html><body><table width='100%'>");
dv.GetHtml(sw1);
sw1.Write("</table></body></html>");
sw1.Close();
dv = null;
orig.Close();
diffGram.Close();
From above code, dv.GetHtml(sw1); this statement gives html file which shows all modified and non modified nodes, but we need to get only modified nodes information.
How can we get only modified modes information?
Any hint, reference would be great help.
Thank You!

How to validate xml, not containing xmlns=..., with c# XmlSerializer?

I am working with Mismo 2.3.1, dtd based schema. I converted the dtd to xsd and then generated c# code to serialize/deserialze object representations of the xml doc.
Given a valid mismo 2.3.1 xml doc, I can deserialize into my generated C# class.
I have code working to use XmlSerializer along with XmlReaderSettings and XmlSchmeas collection, reading in my converted xsd.
If I put xmlns="http://mySchema..." in the root element, and try to validate intentionally invalid xml, works as expected, my validation event gets pinged with accurate description.
If I take out the xmlns attribute, then i get "could not find schema information for element [my root element]"
Any idea on how to validate xml that comes in without the xmlns spec? Any settings to say to the serializer "use this schema when you come across this element"?
Thanks in advance!
static void Main() {
var settings = new XmlReaderSettings();
settings.NameTable = new NameTable();
var nsMgr = new XmlNamespaceManager(settings.NameTable);
nsMgr.AddNamespace("", "http://example.com/2013/ns"); // <-- set default namespace
settings.ValidationType = ValidationType.Schema;
settings.Schemas.Add(null, #"C:\XSDSchema.xsd"); // <-- set schema location for the default namespace
var parserCtx = new XmlParserContext(settings.NameTable, nsMgr, XmlSpace.Default);
using (var reader = XmlReader.Create(#"C:\file.xml", settings, parserCtx)) {
var serializer = new XmlSerializer(typeof(Foo));
Foo f = (Foo)serializer.Deserialize(reader);
}
}

How to prevent XXE attack (XmlDocument in .NET)

We had a security audit on our code, and they mentioned that our code is vulnerable to EXternal Entity (XXE) attack. I am using following code -
string OurOutputXMLString=
"<ce><input><transaction><length>00000</length><tran_type>Login</tran_type></transaction><user><user_id>ce_userid</user_id><subscriber_name>ce_subscribername</subscriber_name><subscriber_id>ce_subscriberid</subscriber_id><group_id>ce_groupid</group_id><permissions></permissions></user><consumer><login_details><username>UnitTester9</username><password>pDhE5AsKBHw85Sqgg6qdKQ==</password><pin>tOlkiae9epM=</pin></login_details></consumer></input></ce>"
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(OurOutputXMLString);
In the audit report they say that it's failing because an XML entity can contain URLs that can resolve outside of intended control. XML entity resolver will attempt to resolve and retrieve external references. If attacker-controlled XML can be submitted to one of these functions, then the attacker could gain access to information about an internal network, local filesystem, or other sensitive data.
To avoid this I wrote the following code but it doesn't work.
MemoryStream stream =
new MemoryStream(System.Text.Encoding.Default.GetBytes(OurOutputXMLString));
XmlReaderSettings settings = new XmlReaderSettings();
settings.DtdProcessing = DtdProcessing.Prohibit;
settings.MaxCharactersFromEntities = 6000;
XmlReader reader = XmlReader.Create(stream, settings);
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(reader);
But I can see here that reader does not have any value to load into xmlDoc(XmlDocument).
Can anyone help where I am missing things?
External resources are resolved using the XmlResolver provided via XmlDocument.XmlResolver property. If your XML documents **should not contain any external resource **(for example DTDs or schemas) simply set this property to null:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.XmlResolver = null;
xmlDoc.LoadXml(OurOutputXMLString);
If you want to filter where these URLs come from (for example to allow only certain domains) just derive your own class from XmlUrlResolver and override the ResolveUri() method. There you can check what the URL is and sanitize it (for example you can allow only URLs within your local network or from trusted sources).
For example:
class CustomUrlResovler : XmlUrlResolver
{
public override Uri ResolveUri(Uri baseUri, string relativeUri)
{
Uri uri = new Uri(baseUri, relativeUri);
if (IsUnsafeHost(uri.Host))
return null;
return base.ResolveUri(baseUri, relativeUri);
}
private bool IsUnsafeHost(string host)
{
return false;
}
}
Where IsUnsafeHost() is a custom function that check if the given host is allowed or not. See this post here on SO for few ideas. Just return null from ResolveUri() to save your code from this kind of attacks. In case the URI is allowed you can simply return the default XmlUrlResolver.ResolveUri() implementation.
To use it:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.XmlResolver = new CustomUrlResolver();
xmlDoc.LoadXml(OurOutputXMLString);
For more details about how XML external resources are resolved just read Resolving External Resources on MS Docs. If your code is more complex than this example then you should definitely read Remarks section for XmlDocument.XmlResolver property.
So its better to use
new XmlDocument { XmlResolver = null };
Interestingly from .net 4.5.2 and 4.6, the default resolver behaves differently and does not use an XmlUrlResolver upfront implicitly to resolve any urls or locations as i seen.
//In pre 4.5.2 it is a security issue.
//In 4.5.2 it will not resolve any more the url references in dtd and such,
//Still better to avoid the below since it will trigger security warnings.
new XmlDocument();
Setting the XmlReaderSettings.DtdProcessing to DtdProcessing.Prohibit works totally fine in .NET 4.7.2. Here is what i used to test.
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE demo
[
<!ELEMENT demo ANY >
<!ENTITY % extentity SYSTEM "https://www.hl7.org/documentcenter/public/wg/structure/CDA.xsl">
%extentity;
]>
<test>
Some random content
</test>
Saved the above content in a file and read the file from the following fragment of c# code.
XmlReaderSettings settings = new XmlReaderSettings();
settings.DtdProcessing = DtdProcessing.Prohibit;
settings.MaxCharactersFromEntities = 6000;
//The following stream should be the filestream of the above content.
XmlReader reader = XmlReader.Create(stream, settings);
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(reader);
I get the following exception.
For security reasons DTD is prohibited in this XML document. To enable DTD
processing set the DtdProcessing property on XmlReaderSettings to Parse and
pass the settings into XmlReader.Create method.
at System.Xml.XmlTextReaderImpl.Throw(Exception e)
at System.Xml.XmlTextReaderImpl.ParseDoctypeDecl()
at System.Xml.XmlTextReaderImpl.ParseDocumentContent()
at System.Xml.XmlLoader.LoadNode(Boolean skipOverWhitespace)
at System.Xml.XmlLoader.LoadDocSequence(XmlDocument parentDoc)
at System.Xml.XmlDocument.Load(XmlReader reader)

An error occurred while parsing EntityName

I'm trying to load a xml document into an object XPathDocument in C#.
My xml documents include this line:
trés dégagée + rade
and when the parser arrives there it gives me this error:
"An error occurred while parsing EntityName"
I know that's normal cause of the character "é". Does anybody know how can I avoid this error... My idea is to insert into the xml document an entities declaration and after replace all special characters with entities...but it's long and I’m not sure if it's working. Do you have other ideas? Simpler?
Thanks a lot
Was about to post this and just then the servers went down. I think I've rewritten it correctly from memory:
I think that the problem lies within the fact that by default the XPathDocument uses an XmlTextReader to parse the contents of the supplied file and this XmlTextReader uses an EntityHandling setting of ExpandEntities.
In other words, when you rely on the default settings, an XmlTextReader will validate the input XML and try to resolve all entities. The better way is to do this manually by taking full control over the XmlReaderSettings (I always do it manually):
string myXMLFile = "SomeFile.xml";
string fileContent = LoadXML(myXMLFile);
private string LoadXML(string xml)
{
XPathDocument xDoc;
XmlReaderSettings xrs = new XmlReaderSettings();
// The following line does the "magic".
xrs.CheckCharacters = false;
using (XmlReader xr = XmlReader.Create(xml, xrs))
{
xDoc = new XPathDocument(xr);
}
if (xDoc != null)
{
XPathNavigator xNav = xDoc.CreateNavigator();
return xNav.OuterXml;
}
else
// Unable to load file
return null;
}
Typically this is caused by a mismatch between the encoding used to read the file and the files actually encoding.
At a guess I would say the file is UTF-8 encoded but you are reading it with a default encoding.
Try beefing up your question with more details to get a more definitive answer.

Categories