import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.net.MalformedURLException;
import java.net.URI;
+import java.net.URISyntaxException;
import java.net.URL;
-import java.util.ArrayList;
-import java.util.List;
import javax.xml.XMLConstants;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.SchemaFactory;
import org.w3c.dom.ls.LSResourceResolver;
import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
/**
- * XMLスキーマの各種ビルダ。
+ * XML schema (XSD) utilities.
+ *
+ * <p>RELAX NG is not supported.
*/
public final class SchemaUtil {
- /**
- * 隠しコンストラクタ。
- */
- private SchemaUtil(){
- assert false;
- throw new AssertionError();
- }
+ /** XML Schema. */
+ public static final String SCHEMA_XML =
+ "http://www.w3.org/2001/xml.xsd";
- /**
- * XML Schema 用のスキーマファクトリを返す。
- * @return スキーマファクトリ
- */
- public static SchemaFactory newSchemaFactory(){
- SchemaFactory result = newSchemaFactory(null);
- return result;
- }
+ /** XSD namespace. */
+ public static final String NS_XSD =
+ "http://www.w3.org/2001/XMLSchema-instance";
- /**
- * XML Schema 用のスキーマファクトリを返す。
- * @param resolver カスタムリゾルバ。nullも可。
- * @return スキーマファクトリ
- */
- public static SchemaFactory newSchemaFactory(
- LSResourceResolver resolver ){
- SchemaFactory schemaFactory =
- SchemaFactory.newInstance(
- XMLConstants.W3C_XML_SCHEMA_NS_URI
- );
+ private static final String LOCAL_SCHEMA_XML =
+ "resources/xmlspace.xsd";
- // schemaFactory.setFeature(name, value);
- // schemaFactory.setProperty(name, object);
+ private static final URI URI_XSD_ORIG;
+ private static final URI URI_XSD_LOCAL;
- schemaFactory.setErrorHandler(BotherHandler.HANDLER);
- schemaFactory.setResourceResolver(resolver);
+ private static final String ALLOWED_USCHEMA = "http";
- return schemaFactory;
+ private static final Class<?> THISCLASS = SchemaUtil.class;
+
+
+ static{
+ URL localXsdUrl = THISCLASS.getResource(LOCAL_SCHEMA_XML);
+ URI localXsdUri;
+ try{
+ localXsdUri = localXsdUrl.toURI();
+ }catch(URISyntaxException e){
+ throw new ExceptionInInitializerError(e);
+ }
+
+ URI_XSD_ORIG = URI.create(SCHEMA_XML);
+ URI_XSD_LOCAL = localXsdUri;
+
+ assert ALLOWED_USCHEMA.equalsIgnoreCase(URI_XSD_ORIG.getScheme());
}
+
/**
- * ローカルリソースをSourceに変換する。
- * @param resource ローカルリソース
- * @return XML Source
- * @throws MalformedURLException 不正なURI
- * @throws IOException オープンエラー
+ * Hidden constructor.
*/
- private static Source toLocalSource(LocalXmlResource resource)
- throws MalformedURLException, IOException{
- URI localUri = resource.getLocalResource();
- URL localUrl = localUri.toURL();
+ private SchemaUtil(){
+ assert false;
+ throw new AssertionError();
+ }
- InputStream is = localUrl.openStream();
- is = new BufferedInputStream(is);
- Source result = new StreamSource(is);
+ /**
+ * build xml.xsd redirection info.
+ *
+ * @return resolver
+ */
+ public static XmlResourceResolver buildXmlXsdResolver(){
+ XmlResourceResolver result = new XmlResourceResolver();
+ result.putRedirected(URI_XSD_ORIG, URI_XSD_LOCAL);
return result;
}
/**
- * ローカルリソース群をSource群に変換する。
- * @param resArray ローカルリソースURI並び
- * @return XML Source並び
- * @throws MalformedURLException 不正なURI
- * @throws IOException オープンエラー
+ * Build SchemaFactory for XML Schema but safety.
+ *
+ * <p>Includes some considerations for XXE vulnerabilities.
+ *
+ * <p>Restrict access to
+ * External Entity Reference & external DTDs
+ * in xml schema file.
+ *
+ * <p>Restrict access to External schema file access in xml schema file,
+ * but HTTP access is allowed.
+ * This special limit considers access to
+ * importing http://www.w3.org/2001/xml.xsd
+ * in top of common xml schema file.
+ *
+ * @return schema factory
*/
- private static Source[] toLocalSourceArray(LocalXmlResource... resArray)
- throws MalformedURLException, IOException{
- List<Source> sourceList = new ArrayList<>(resArray.length);
+ public static SchemaFactory newSchemaFactory(){
+ SchemaFactory schemaFactory;
+ schemaFactory = SchemaFactory.newInstance(
+ XMLConstants.W3C_XML_SCHEMA_NS_URI);
- for(LocalXmlResource resource : resArray){
- Source localSource = toLocalSource(resource);
- sourceList.add(localSource);
+ try{
+ // Prevent denial-of-service attack.
+ schemaFactory.setFeature(
+ XMLConstants.FEATURE_SECURE_PROCESSING, true);
+ }catch(SAXNotRecognizedException | SAXNotSupportedException e){
+ // THIS FEATURE MUST BE SUPPORTED
+ assert false;
}
- Source[] result = new Source[sourceList.size()];
- result = sourceList.toArray(result);
- return result;
+ try{
+ // Disallow external entity reference & external DTD access.
+ schemaFactory.setProperty(
+ XMLConstants.ACCESS_EXTERNAL_DTD, "");
+ // Allow only HTTP external schema file.
+ schemaFactory.setProperty(
+ XMLConstants.ACCESS_EXTERNAL_SCHEMA, ALLOWED_USCHEMA);
+ }catch(SAXNotRecognizedException | SAXNotSupportedException e){
+ // THIS PROPERTY MUST BE SUPPORTED JAXP1.5 or later
+ assert false;
+ }
+
+ LSResourceResolver resolver = buildXmlXsdResolver();
+ schemaFactory.setResourceResolver(resolver);
+
+ schemaFactory.setErrorHandler(BotherHandler.HANDLER);
+
+ return schemaFactory;
}
/**
- * ローカルスキーマをロードする。
+ * pre-load & pre-compile local schema files.
*
- * <p>任意のリゾルバを指定可能
- *
- * @param resolver リゾルバ
- * @param resArray ローカルスキーマ情報並び
- * @return スキーマ
+ * @param localSchemaUris local schema resources.
+ * @return schema
+ * @throws SAXException invalid schema definition
+ * @throws IOException local resource i/o error
*/
- public static Schema newSchema(XmlResourceResolver resolver,
- LocalXmlResource... resArray ){
- for(LocalXmlResource resource : resArray){
- resolver.putRedirected(resource);
+ public static Schema newSchema(URI... localSchemaUris)
+ throws SAXException, IOException{
+ SchemaFactory schemaFactory = newSchemaFactory();
+
+ int uris = localSchemaUris.length;
+ if(uris <= 0){
+ // on-demand xml-schema with schemaLocation attribute.
+ return schemaFactory.newSchema();
}
- Source[] sources;
- try{
- sources = toLocalSourceArray(resArray);
- }catch(IOException e){ // ビルド障害
- assert false;
- throw new AssertionError(e);
+ InputStream[] iss = new InputStream[uris];
+ for(int idx = 0; idx < uris; idx++){
+ URI localUri = localSchemaUris[idx];
+ URL localUrl = localUri.toURL();
+ InputStream is;
+ is = localUrl.openStream();
+ is = new BufferedInputStream(is);
+ iss[idx] = is;
}
- SchemaFactory schemaFactory = newSchemaFactory(resolver);
+ Source[] sources = new Source[uris];
+ for(int idx = 0; idx < uris; idx++){
+ InputStream is = iss[idx];
+ sources[idx] = new StreamSource(is);
+ }
Schema result;
try{
- if(sources.length <= 0){
- // ドキュメント埋め込みスキーマURLにリゾルバ経由でアクセス
- result = schemaFactory.newSchema();
- }else{
- result = schemaFactory.newSchema(sources);
+ result = schemaFactory.newSchema(sources);
+ }finally{
+ for(InputStream is : iss){
+ is.close();
}
- }catch(SAXException e){ // Build error
- assert false;
- throw new AssertionError(e);
}
- // TODO: Sourceを閉めるのは誰の責務?
-
return result;
}