View Javadoc
1   /*
2    * This file is part of dependency-check-utils.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *
16   * Copyright (c) 2016 Jeremy Long. All Rights Reserved.
17   */
18  package org.owasp.dependencycheck.utils;
19  
20  import java.io.InputStream;
21  import javax.xml.XMLConstants;
22  import javax.xml.parsers.DocumentBuilder;
23  import javax.xml.parsers.DocumentBuilderFactory;
24  import javax.xml.parsers.ParserConfigurationException;
25  import javax.xml.parsers.SAXParser;
26  import javax.xml.parsers.SAXParserFactory;
27  import org.xml.sax.SAXException;
28  import org.xml.sax.SAXNotRecognizedException;
29  import org.xml.sax.SAXNotSupportedException;
30  import org.xml.sax.SAXParseException;
31  
32  /**
33   * Collection of XML related code.
34   *
35   * @author Jeremy Long
36   * @version $Id: $Id
37   */
38  public final class XmlUtils {
39  
40      /**
41       * JAXP Schema Language. Source:
42       * http://docs.oracle.com/javase/tutorial/jaxp/sax/validation.html
43       */
44      public static final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
45      /**
46       * W3C XML Schema. Source:
47       * http://docs.oracle.com/javase/tutorial/jaxp/sax/validation.html
48       */
49      public static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema";
50      /**
51       * JAXP Schema Source. Source:
52       * http://docs.oracle.com/javase/tutorial/jaxp/sax/validation.html
53       */
54      public static final String JAXP_SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource";
55  
56      /**
57       * Private constructor for a utility class.
58       */
59      private XmlUtils() {
60      }
61  
62      /**
63       * Constructs a validating secure SAX Parser.
64       *
65       * @param schemaStream One or more inputStreams with the schema(s) that the
66       * parser should be able to validate the XML against, one InputStream per
67       * schema
68       * @return a SAX Parser
69       * @throws javax.xml.parsers.ParserConfigurationException is thrown if there
70       * is a parser configuration exception
71       * @throws org.xml.sax.SAXNotRecognizedException thrown if there is an
72       * unrecognized feature
73       * @throws org.xml.sax.SAXNotSupportedException thrown if there is a
74       * non-supported feature
75       * @throws org.xml.sax.SAXException is thrown if there is a
76       * org.xml.sax.SAXException
77       */
78      public static SAXParser buildSecureSaxParser(InputStream... schemaStream) throws ParserConfigurationException,
79              SAXNotRecognizedException, SAXNotSupportedException, SAXException {
80          final SAXParserFactory factory = SAXParserFactory.newInstance();
81          factory.setNamespaceAware(true);
82          factory.setValidating(true);
83          factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
84          factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
85          factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
86          factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
87          factory.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
88          factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
89  
90          String accessExternalSchema = System.getProperty("javax.xml.accessExternalSchema");
91          if (accessExternalSchema == null) {
92              accessExternalSchema = "file, https";
93          } else if (!"ALL".equalsIgnoreCase(accessExternalSchema)) {
94              if (!accessExternalSchema.contains("file")) {
95                  accessExternalSchema += ", file";
96              }
97              if (!accessExternalSchema.contains("https")) {
98                  accessExternalSchema += ", https";
99              }
100         }
101         System.setProperty("javax.xml.accessExternalSchema", accessExternalSchema);
102 
103         final SAXParser saxParser = factory.newSAXParser();
104         saxParser.setProperty(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);
105         saxParser.setProperty(JAXP_SCHEMA_SOURCE, schemaStream);
106         return saxParser;
107     }
108 
109     /**
110      * Converts an attribute value representing an xsd:boolean value to a
111      * boolean using the rules as stated in the XML specification.
112      *
113      * @param lexicalXSDBoolean The string-value of the boolean
114      * @return the boolean value represented by {@code lexicalXSDBoolean}
115      * @throws java.lang.IllegalArgumentException When {@code lexicalXSDBoolean}
116      * does fit the lexical space of the XSD boolean datatype
117      */
118     public static boolean parseBoolean(String lexicalXSDBoolean) {
119         final boolean result;
120         switch (lexicalXSDBoolean) {
121             case "true":
122             case "1":
123                 result = true;
124                 break;
125             case "false":
126             case "0":
127                 result = false;
128                 break;
129             default:
130                 throw new IllegalArgumentException("'" + lexicalXSDBoolean + "' is not a valid xs:boolean value");
131         }
132         return result;
133     }
134 
135     /**
136      * Constructs a secure SAX Parser.
137      *
138      * @return a SAX Parser
139      * @throws javax.xml.parsers.ParserConfigurationException thrown if there is
140      * a parser configuration exception
141      * @throws org.xml.sax.SAXNotRecognizedException thrown if there is an
142      * unrecognized feature
143      * @throws org.xml.sax.SAXNotSupportedException thrown if there is a
144      * non-supported feature
145      * @throws org.xml.sax.SAXException is thrown if there is a
146      * org.xml.sax.SAXException
147      */
148     public static SAXParser buildSecureSaxParser() throws ParserConfigurationException,
149             SAXNotRecognizedException, SAXNotSupportedException, SAXException {
150         final SAXParserFactory factory = SAXParserFactory.newInstance();
151         factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
152         factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
153         factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
154         return factory.newSAXParser();
155     }
156 
157     /**
158      * Constructs a new document builder with security features enabled.
159      *
160      * @return a new document builder
161      * @throws javax.xml.parsers.ParserConfigurationException thrown if there is
162      * a parser configuration exception
163      */
164     public static DocumentBuilder buildSecureDocumentBuilder() throws ParserConfigurationException {
165         final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
166         factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
167         factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
168         factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
169         return factory.newDocumentBuilder();
170     }
171 
172     /**
173      * Builds a prettier exception message.
174      *
175      * @param ex the SAXParseException
176      * @return an easier to read exception message
177      */
178     public static String getPrettyParseExceptionInfo(SAXParseException ex) {
179 
180         final StringBuilder sb = new StringBuilder();
181 
182         if (ex.getSystemId() != null) {
183             sb.append("systemId=").append(ex.getSystemId()).append(", ");
184         }
185         if (ex.getPublicId() != null) {
186             sb.append("publicId=").append(ex.getPublicId()).append(", ");
187         }
188         if (ex.getLineNumber() > 0) {
189             sb.append("Line=").append(ex.getLineNumber());
190         }
191         if (ex.getColumnNumber() > 0) {
192             sb.append(", Column=").append(ex.getColumnNumber());
193         }
194         sb.append(": ").append(ex.getMessage());
195 
196         return sb.toString();
197     }
198 }