4 * License : The MIT License
5 * Copyright(c) 2013 MikuToga Partners
8 package jp.sfjp.mikutoga.xml;
10 import java.io.BufferedInputStream;
11 import java.io.IOException;
12 import java.io.InputStream;
13 import java.net.MalformedURLException;
16 import java.util.ArrayList;
17 import java.util.List;
18 import javax.xml.XMLConstants;
19 import javax.xml.transform.Source;
20 import javax.xml.transform.stream.StreamSource;
21 import javax.xml.validation.Schema;
22 import javax.xml.validation.SchemaFactory;
23 import org.w3c.dom.ls.LSResourceResolver;
24 import org.xml.sax.SAXException;
25 import org.xml.sax.SAXNotRecognizedException;
26 import org.xml.sax.SAXNotSupportedException;
31 public final class SchemaUtil {
35 public static final String SCHEMA_XML =
36 "http://www.w3.org/2001/xml.xsd";
39 public static final String NS_XSD =
40 "http://www.w3.org/2001/XMLSchema-instance";
42 private static final String LOCAL_SCHEMA_XML =
43 "resources/xmlspace.xsd";
45 private static final URI URI_XSD_ORIG;
46 private static final URI URI_XSD_LOCAL;
48 private static final String ALLOWED_USCHEMA = "http";
50 private static final Class<?> THISCLASS = SchemaUtil.class;
54 URL redirectRes = THISCLASS.getResource(LOCAL_SCHEMA_XML);
55 String redirectResName = redirectRes.toString();
57 URI_XSD_ORIG = URI.create(SCHEMA_XML);
58 URI_XSD_LOCAL = URI.create(redirectResName);
60 assert ALLOWED_USCHEMA.equalsIgnoreCase(URI_XSD_ORIG.getScheme());
69 throw new AssertionError();
74 * build xml.xsd redirection info.
78 public static XmlResourceResolver buildXmlXsdResolver(){
79 XmlResourceResolver result = new XmlResourceResolver();
80 result.putRedirected(URI_XSD_ORIG, URI_XSD_LOCAL);
85 * Build SchemaFactory for XML Schema but safety.
87 * <p>Includes some considerations for XXE vulnerabilities.
89 * <p>Restrict access to
90 * External Entity Reference & external DTDs
93 * <p>Restrict access to External schema file access in xml schema file,
94 * but HTTP access is allowed.
95 * This special limit considers access to
96 * importing http://www.w3.org/2001/xml.xsd
97 * in top of common xml schema file.
98 * If HTTP access controll is needed, customize resolver yourself.
100 * @param resolver Custom resolver for reading xml schema.
101 * Resolve reference to nothing if null.
102 * @return schema factory
104 public static SchemaFactory newSchemaFactory(
105 LSResourceResolver resolver ){
106 SchemaFactory schemaFactory;
107 schemaFactory = SchemaFactory.newInstance(
108 XMLConstants.W3C_XML_SCHEMA_NS_URI);
111 // Prevent denial of service attack.
112 schemaFactory.setFeature(
113 XMLConstants.FEATURE_SECURE_PROCESSING, true);
114 }catch(SAXNotRecognizedException | SAXNotSupportedException e){
115 // FEATURE MUST BE SUPPORTED
120 // Disallow external entity reference & external DTD access.
121 schemaFactory.setProperty(
122 XMLConstants.ACCESS_EXTERNAL_DTD, "");
123 // Allow only HTTP external schema file.
124 schemaFactory.setProperty(
125 XMLConstants.ACCESS_EXTERNAL_SCHEMA, ALLOWED_USCHEMA);
126 }catch(SAXNotRecognizedException | SAXNotSupportedException e){
127 // PROPERTY MUST BE SUPPORTED JAXP1.5 or later
131 schemaFactory.setResourceResolver(resolver);
133 schemaFactory.setErrorHandler(BotherHandler.HANDLER);
135 return schemaFactory;
139 * ローカルリソースをSourceに変換する。
140 * @param resource ローカルリソース
142 * @throws MalformedURLException 不正なURI
143 * @throws IOException オープンエラー
145 private static Source toLocalSource(LocalXmlResource resource)
146 throws MalformedURLException, IOException{
147 URI localUri = resource.getLocalResource();
148 URL localUrl = localUri.toURL();
150 InputStream is = localUrl.openStream();
151 is = new BufferedInputStream(is);
153 Source result = new StreamSource(is);
158 * ローカルリソース群をSource群に変換する。
159 * @param resArray ローカルリソースURI並び
160 * @return XML Source並び
161 * @throws MalformedURLException 不正なURI
162 * @throws IOException オープンエラー
164 private static Source[] toLocalSourceArray(LocalXmlResource... resArray)
165 throws MalformedURLException, IOException{
166 List<Source> sourceList = new ArrayList<>(resArray.length);
168 for(LocalXmlResource resource : resArray){
169 Source localSource = toLocalSource(resource);
170 sourceList.add(localSource);
173 Source[] result = new Source[sourceList.size()];
174 result = sourceList.toArray(result);
183 * @param resolver リゾルバ
184 * @param resArray ローカルスキーマ情報並び
187 public static Schema newSchema(XmlResourceResolver resolver,
188 LocalXmlResource... resArray ){
189 for(LocalXmlResource resource : resArray){
190 resolver.putRedirected(resource);
195 sources = toLocalSourceArray(resArray);
196 }catch(IOException e){ // ビルド障害
198 throw new AssertionError(e);
201 SchemaFactory schemaFactory = newSchemaFactory(resolver);
205 if(sources.length <= 0){
206 // ドキュメント埋め込みスキーマURLにリゾルバ経由でアクセス
207 result = schemaFactory.newSchema();
209 result = schemaFactory.newSchema(sources);
211 }catch(SAXException e){ // Build error
213 throw new AssertionError(e);
216 // TODO: Sourceを閉めるのは誰の責務?