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;
29 * XML schema (XSD) utilities.
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.
99 * @return schema factory
101 public static SchemaFactory newSchemaFactory(){
102 SchemaFactory schemaFactory;
103 schemaFactory = SchemaFactory.newInstance(
104 XMLConstants.W3C_XML_SCHEMA_NS_URI);
107 // Prevent denial of service attack.
108 schemaFactory.setFeature(
109 XMLConstants.FEATURE_SECURE_PROCESSING, true);
110 }catch(SAXNotRecognizedException | SAXNotSupportedException e){
111 // FEATURE MUST BE SUPPORTED
116 // Disallow external entity reference & external DTD access.
117 schemaFactory.setProperty(
118 XMLConstants.ACCESS_EXTERNAL_DTD, "");
119 // Allow only HTTP external schema file.
120 schemaFactory.setProperty(
121 XMLConstants.ACCESS_EXTERNAL_SCHEMA, ALLOWED_USCHEMA);
122 }catch(SAXNotRecognizedException | SAXNotSupportedException e){
123 // PROPERTY MUST BE SUPPORTED JAXP1.5 or later
127 LSResourceResolver resolver = buildXmlXsdResolver();
128 schemaFactory.setResourceResolver(resolver);
130 schemaFactory.setErrorHandler(BotherHandler.HANDLER);
132 return schemaFactory;
136 * ローカルリソースをSourceに変換する。
137 * @param resource ローカルリソース
139 * @throws MalformedURLException 不正なURI
140 * @throws IOException オープンエラー
142 private static Source toLocalSource(LocalXmlResource resource)
143 throws MalformedURLException, IOException{
144 URI localUri = resource.getLocalResource();
145 URL localUrl = localUri.toURL();
147 InputStream is = localUrl.openStream();
148 is = new BufferedInputStream(is);
150 Source result = new StreamSource(is);
155 * ローカルリソース群をSource群に変換する。
156 * @param resArray ローカルリソースURI並び
157 * @return XML Source並び
158 * @throws MalformedURLException 不正なURI
159 * @throws IOException オープンエラー
161 private static Source[] toLocalSourceArray(LocalXmlResource... resArray)
162 throws MalformedURLException, IOException{
163 List<Source> sourceList = new ArrayList<>(resArray.length);
165 for(LocalXmlResource resource : resArray){
166 Source localSource = toLocalSource(resource);
167 sourceList.add(localSource);
170 Source[] result = new Source[sourceList.size()];
171 result = sourceList.toArray(result);
180 * @param resArray ローカルスキーマ情報並び
183 public static Schema newSchema(LocalXmlResource... resArray){
186 sources = toLocalSourceArray(resArray);
187 }catch(IOException e){ // ビルド障害
189 throw new AssertionError(e);
192 SchemaFactory schemaFactory = newSchemaFactory();
196 if(sources.length <= 0){
197 // ドキュメント埋め込みスキーマURLにリゾルバ経由でアクセス
198 result = schemaFactory.newSchema();
200 result = schemaFactory.newSchema(sources);
202 }catch(SAXException e){ // Build error
204 throw new AssertionError(e);
207 // TODO: Sourceを閉めるのは誰の責務?