1 /*
2  * This file is part of dependency-check-core.
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  */
37 public final class XmlUtils {
38 
39     /**
40      * JAXP Schema Language. Source:
41      * http://docs.oracle.com/javase/tutorial/jaxp/sax/validation.html
42      */
43     public static final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
44     /**
45      * W3C XML Schema. Source:
46      * http://docs.oracle.com/javase/tutorial/jaxp/sax/validation.html
47      */
48     public static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema";
49     /**
50      * JAXP Schema Source. Source:
51      * http://docs.oracle.com/javase/tutorial/jaxp/sax/validation.html
52      */
53     public static final String JAXP_SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource";
54 
55     /**
56      * Private constructor for a utility class.
57      */
XmlUtils()58     private XmlUtils() {
59     }
60 
61     /**
62      * Constructs a validating secure SAX Parser.
63      *
64      * @param schemaStream One or more inputStreams with the schema(s) that the
65      * parser should be able to validate the XML against, one InputStream per
66      * schema
67      * @return a SAX Parser
68      * @throws ParserConfigurationException is thrown if there is a parser
69      * configuration exception
70      * @throws SAXNotRecognizedException thrown if there is an unrecognized
71      * feature
72      * @throws SAXNotSupportedException thrown if there is a non-supported
73      * feature
74      * @throws SAXException is thrown if there is a SAXException
75      */
buildSecureSaxParser(InputStream... schemaStream)76     public static SAXParser buildSecureSaxParser(InputStream... schemaStream) throws ParserConfigurationException,
77             SAXNotRecognizedException, SAXNotSupportedException, SAXException {
78         final SAXParserFactory factory = SAXParserFactory.newInstance();
79         factory.setNamespaceAware(true);
80         factory.setValidating(true);
81         factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
82         factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
83         //setting the following unfortunately breaks reading the old suppression files (version 1).
84         //factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
85 
86         final SAXParser saxParser = factory.newSAXParser();
87         saxParser.setProperty(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);
88         saxParser.setProperty(JAXP_SCHEMA_SOURCE, schemaStream);
89         return saxParser;
90     }
91 
92     /**
93      * Converts an attribute value representing an xsd:boolean value to a
94      * boolean using the rules as stated in the XML specification.
95      *
96      * @param lexicalXSDBoolean The string-value of the boolean
97      * @return the boolean value represented by {@code lexicalXSDBoolean}
98      * @throws IllegalArgumentException When {@code lexicalXSDBoolean} does fit
99      * the lexical space of the XSD boolean datatype
100      */
parseBoolean(String lexicalXSDBoolean)101     public static boolean parseBoolean(String lexicalXSDBoolean) {
102         final boolean result;
103         switch (lexicalXSDBoolean) {
104             case "true":
105             case "1":
106                 result = true;
107                 break;
108             case "false":
109             case "0":
110                 result = false;
111                 break;
112             default:
113                 throw new IllegalArgumentException("'" + lexicalXSDBoolean + "' is not a valid xs:boolean value");
114         }
115         return result;
116     }
117 
118     /**
119      * Constructs a secure SAX Parser.
120      *
121      * @return a SAX Parser
122      * @throws ParserConfigurationException thrown if there is a parser
123      * configuration exception
124      * @throws SAXNotRecognizedException thrown if there is an unrecognized
125      * feature
126      * @throws SAXNotSupportedException thrown if there is a non-supported
127      * feature
128      * @throws SAXException is thrown if there is a SAXException
129      */
buildSecureSaxParser()130     public static SAXParser buildSecureSaxParser() throws ParserConfigurationException,
131             SAXNotRecognizedException, SAXNotSupportedException, SAXException {
132         final SAXParserFactory factory = SAXParserFactory.newInstance();
133         factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
134         factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
135         factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
136         return factory.newSAXParser();
137     }
138 
139     /**
140      * Constructs a new document builder with security features enabled.
141      *
142      * @return a new document builder
143      * @throws ParserConfigurationException thrown if there is a parser
144      * configuration exception
145      */
buildSecureDocumentBuilder()146     public static DocumentBuilder buildSecureDocumentBuilder() throws ParserConfigurationException {
147         final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
148         factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
149         factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
150         factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
151         return factory.newDocumentBuilder();
152     }
153 
154     /**
155      * Builds a prettier exception message.
156      *
157      * @param ex the SAXParseException
158      * @return an easier to read exception message
159      */
getPrettyParseExceptionInfo(SAXParseException ex)160     public static String getPrettyParseExceptionInfo(SAXParseException ex) {
161 
162         final StringBuilder sb = new StringBuilder();
163 
164         if (ex.getSystemId() != null) {
165             sb.append("systemId=").append(ex.getSystemId()).append(", ");
166         }
167         if (ex.getPublicId() != null) {
168             sb.append("publicId=").append(ex.getPublicId()).append(", ");
169         }
170         if (ex.getLineNumber() > 0) {
171             sb.append("Line=").append(ex.getLineNumber());
172         }
173         if (ex.getColumnNumber() > 0) {
174             sb.append(", Column=").append(ex.getColumnNumber());
175         }
176         sb.append(": ").append(ex.getMessage());
177 
178         return sb.toString();
179     }
180 }
181