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