Skip to content

Commit a6f4657

Browse files
authored
Validate PatternParser DTDs against expected name (#1358)
1 parent 7b73146 commit a6f4657

2 files changed

Lines changed: 117 additions & 5 deletions

File tree

src/Lucene.Net.Analysis.Common/Analysis/Compound/Hyphenation/PatternParser.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -364,19 +364,23 @@ protected static string GetInterletterValues(string pat)
364364

365365
/// <summary>
366366
/// LUCENENET specific helper class to force the DTD file to be read from the embedded resource
367-
/// rather than from the file system.
367+
/// rather than from the file system. Any other external reference is rejected, so the parser
368+
/// only ever resolves the known, embedded <c>hyphenation.dtd</c>.
368369
/// </summary>
369370
internal class DtdResolver : XmlUrlResolver
370371
{
372+
internal const string DTD_FILENAME = "hyphenation.dtd";
373+
371374
public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn)
372375
{
373-
string dtdFilename = "hyphenation.dtd";
374-
if (dtdFilename.Equals(absoluteUri.Segments.LastOrDefault(), StringComparison.Ordinal))
376+
if (DTD_FILENAME.Equals(absoluteUri?.Segments.LastOrDefault(), StringComparison.Ordinal))
375377
{
376-
return typeof(PatternParser).FindAndGetManifestResourceStream(dtdFilename);
378+
return typeof(PatternParser).FindAndGetManifestResourceStream(DTD_FILENAME);
377379
}
378380

379-
return base.GetEntity(absoluteUri, role, ofObjectToReturn);
381+
// Only the embedded hyphenation.dtd is a valid external reference. Reject anything
382+
// else rather than resolving it from the file system or network.
383+
throw new XmlException($"Unexpected external reference in hyphenation data: '{absoluteUri}'. Only '{DTD_FILENAME}' may be referenced.");
380384
}
381385
}
382386

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// Lucene version compatibility level 4.8.1
2+
using Lucene.Net.Analysis.Compound.Hyphenation;
3+
using Lucene.Net.Attributes;
4+
using Lucene.Net.Util;
5+
using NUnit.Framework;
6+
using System;
7+
using System.Collections.Generic;
8+
using System.IO;
9+
using System.Text;
10+
using System.Xml;
11+
12+
namespace Lucene.Net.Analysis.Compound
13+
{
14+
/*
15+
* Licensed to the Apache Software Foundation (ASF) under one or more
16+
* contributor license agreements. See the NOTICE file distributed with
17+
* this work for additional information regarding copyright ownership.
18+
* The ASF licenses this file to You under the Apache License, Version 2.0
19+
* (the "License"); you may not use this file except in compliance with
20+
* the License. You may obtain a copy of the License at
21+
*
22+
* http://www.apache.org/licenses/LICENSE-2.0
23+
*
24+
* Unless required by applicable law or agreed to in writing, software
25+
* distributed under the License is distributed on an "AS IS" BASIS,
26+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
27+
* See the License for the specific language governing permissions and
28+
* limitations under the License.
29+
*/
30+
31+
[LuceneNetSpecific]
32+
public class TestPatternParser : LuceneTestCase
33+
{
34+
/// <summary>
35+
/// A well-formed hyphenation file that references the standard, embedded
36+
/// <c>hyphenation.dtd</c> parses without error.
37+
/// </summary>
38+
[Test]
39+
public virtual void TestValidHyphenationDataParses()
40+
{
41+
using var stream = this.GetType().getResourceAsStream("da_UTF8.xml");
42+
var parser = new PatternParser(new NoOpPatternConsumer());
43+
44+
Assert.DoesNotThrow(() => parser.Parse(stream));
45+
}
46+
47+
/// <summary>
48+
/// A hyphenation file that references an external entity other than the
49+
/// embedded <c>hyphenation.dtd</c> is rejected rather than resolving the
50+
/// reference.
51+
/// </summary>
52+
[Test]
53+
public virtual void TestExternalEntityIsRejected()
54+
{
55+
// Point the external reference at a real, readable file. If the reference were
56+
// resolved, its contents would be pulled into the parsed document; instead the
57+
// parser must refuse the reference.
58+
FileInfo target = CreateTempFile("lucene_pp_", ".txt");
59+
File.WriteAllText(target.FullName, "marker-contents");
60+
61+
string targetUri = new Uri(target.FullName).AbsoluteUri;
62+
string xml =
63+
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
64+
"<!DOCTYPE hyphenation-info [\n" +
65+
" <!ENTITY ext SYSTEM \"" + targetUri + "\">\n" +
66+
"]>\n" +
67+
"<hyphenation-info>\n" +
68+
" <exceptions>&ext;</exceptions>\n" +
69+
"</hyphenation-info>\n";
70+
71+
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(xml));
72+
var parser = new PatternParser(new NoOpPatternConsumer());
73+
74+
Assert.Throws<XmlException>(() => parser.Parse(stream));
75+
}
76+
77+
/// <summary>
78+
/// A reference to an external DTD other than the embedded
79+
/// <c>hyphenation.dtd</c> is rejected.
80+
/// </summary>
81+
[Test]
82+
public virtual void TestExternalDtdIsRejected()
83+
{
84+
FileInfo target = CreateTempFile("lucene_pp_", ".dtd");
85+
File.WriteAllText(target.FullName, "<!ELEMENT hyphenation-info ANY>");
86+
87+
string targetUri = new Uri(target.FullName).AbsoluteUri;
88+
string xml =
89+
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
90+
"<!DOCTYPE hyphenation-info SYSTEM \"" + targetUri + "\">\n" +
91+
"<hyphenation-info></hyphenation-info>\n";
92+
93+
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(xml));
94+
var parser = new PatternParser(new NoOpPatternConsumer());
95+
96+
Assert.Throws<XmlException>(() => parser.Parse(stream));
97+
}
98+
99+
private sealed class NoOpPatternConsumer : IPatternConsumer
100+
{
101+
public void AddClass(string chargroup) { }
102+
103+
public void AddException(string word, IList<object> hyphenatedword) { }
104+
105+
public void AddPattern(string pattern, string values) { }
106+
}
107+
}
108+
}

0 commit comments

Comments
 (0)