Skip to content

Commit 0c52afb

Browse files
authored
More test cases for DTD validation (#115)
1 parent 48b826d commit 0c52afb

File tree

3 files changed

+88
-2
lines changed

3 files changed

+88
-2
lines changed

src/document.mts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,15 +132,22 @@ function parse<Input>(
132132
xmlOptions,
133133
);
134134
try {
135-
if (!xml) {
136-
const errDetails = error.storage.get(errIndex);
135+
const errDetails = error.storage.get(errIndex);
136+
if (errDetails.length > 0) {
137+
if (!xml) {
138+
xmlFreeDoc(xml);
139+
}
137140
throw new XmlParseError(errDetails!.map((d) => d.message).join(''), errDetails!);
138141
}
139142
} finally {
140143
error.storage.free(errIndex);
141144
xmlFreeParserCtxt(ctxt);
142145
}
143146

147+
if (!xml) {
148+
// no error from libxml2, but failed to parse. Usually due to invalid input.
149+
throw new XmlParseError('Failed to parse XML', []);
150+
}
144151
const xmlDocument = XmlDocument.getInstance(xml);
145152
if (xmlOptions & ParseOption.XML_PARSE_XINCLUDE) {
146153
xmlDocument.processXInclude();

test/crossplatform/document.spec.mts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { expect } from 'chai';
22
import {
3+
ParseOption,
34
XmlBufferInputProvider,
45
xmlCleanupInputProvider,
56
XmlDocument,
67
XmlElement,
78
XmlError,
89
XmlNode,
10+
XmlParseError,
911
xmlRegisterInputProvider,
1012
} from '@libxml2-wasm/lib/index.mjs';
1113
import { XmlStringOutputBufferHandler } from '@libxml2-wasm/lib/utils.mjs';
@@ -201,6 +203,25 @@ describe('XmlDocument', () => {
201203
using xml = XmlDocument.fromString('<docs><doc/></docs>');
202204
expect(xml.dtd).to.be.null;
203205
});
206+
207+
it('should validate dtd', () => {
208+
// missing element heading and body
209+
expect(() => XmlDocument.fromString(
210+
`<?xml version="1.0"?>
211+
<!DOCTYPE note [
212+
<!ELEMENT note (to,from,heading,body)>
213+
<!ELEMENT to (#PCDATA)>
214+
<!ELEMENT from (#PCDATA)>
215+
<!ELEMENT heading (#PCDATA)>
216+
<!ELEMENT body (#PCDATA)>
217+
]>
218+
<note>
219+
<to>Tove</to>
220+
<from>Jani</from>
221+
</note>`,
222+
{ option: ParseOption.XML_PARSE_DTDVALID },
223+
)).to.throw(XmlParseError);
224+
});
204225
});
205226

206227
describe('eval', () => {

test/crossplatform/dtd.spec.mts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
import { expect } from 'chai';
22
import {
33
DtdValidator,
4+
ParseOption,
5+
XmlBufferInputProvider,
6+
xmlCleanupInputProvider,
47
XmlDocument,
58
XmlDtd,
69
XmlParseError,
10+
xmlRegisterInputProvider,
11+
XmlValidateError,
712
} from '@libxml2-wasm/lib/index.mjs';
813

914
describe('XmlDtd', () => {
@@ -105,4 +110,57 @@ describe('XmlDtd', () => {
105110
<ELEMENT from (#PCDATA)>
106111
`)).to.throw(XmlParseError, 'Content error in the external subset');
107112
});
113+
114+
it('fails DTD validation when document does not conform', () => {
115+
using dtd = XmlDtd.fromString(`
116+
<!ELEMENT note (to,from,heading,body)>
117+
<!ELEMENT to (#PCDATA)>
118+
<!ELEMENT from (#PCDATA)>
119+
<!ELEMENT heading (#PCDATA)>
120+
<!ELEMENT body (#PCDATA)>
121+
`);
122+
using validator = new DtdValidator(dtd);
123+
124+
// XML document missing required 'heading' and 'body' elements
125+
using xml = XmlDocument.fromString(`\
126+
<?xml version="1.0"?>
127+
<note>
128+
<to>Tove</to>
129+
<from>Jani</from>
130+
</note>`);
131+
132+
expect(() => validator.validate(xml)).to.throw(XmlValidateError);
133+
});
134+
135+
describe('load external subset', () => {
136+
afterEach(() => {
137+
xmlCleanupInputProvider();
138+
});
139+
140+
it('validate with external subset', () => {
141+
const buffers = new XmlBufferInputProvider({
142+
'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd': new TextEncoder().encode(`\
143+
<!ELEMENT note (to,from,heading,body)>
144+
<!ELEMENT to (#PCDATA)>
145+
<!ELEMENT from (#PCDATA)>
146+
<!ELEMENT heading (#PCDATA)>
147+
<!ELEMENT body (#PCDATA)>
148+
`),
149+
});
150+
xmlRegisterInputProvider(buffers);
151+
152+
// missing element heading and body
153+
using xml = XmlDocument.fromString(`\
154+
<?xml version="1.0"?>
155+
<!DOCTYPE html PUBLIC
156+
"-//W3C//DTD XHTML 1.0 Transitional//EN"
157+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
158+
<note>
159+
<to>Tove</to>
160+
<from>Jani</from>
161+
</note>`, { option: ParseOption.XML_PARSE_DTDLOAD }); // this option is required to load external subset
162+
using validator = new DtdValidator(xml.dtd!);
163+
expect(() => validator.validate(xml)).to.throw(XmlValidateError);
164+
});
165+
});
108166
});

0 commit comments

Comments
 (0)