org.wso2.carbon.bpmn.core.types.datatypes.xml.Utils.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.bpmn.core.types.datatypes.xml.Utils.java

Source

/*
 * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * WSO2 Inc. licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 *
 */

package org.wso2.carbon.bpmn.core.types.datatypes.xml;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xerces.impl.Constants;
import org.apache.xerces.util.SecurityManager;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.wso2.carbon.bpmn.core.types.datatypes.xml.api.XMLDocument;
import org.xml.sax.SAXException;

import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;

/**
 * Util class holding util functions related to XML processing
 */
public class Utils {

    private static final Log log = LogFactory.getLog(Utils.class);
    private static final int ENTITY_EXPANSION_LIMIT = 0;

    /**
     * Function to parse string to XMLDocument Object
     *
     * @param str string containing xml to parse
     * @return XMLDocument object which is implementation of org.w3c.dom.Document
     * @throws ParserConfigurationException
     * @throws IOException                  If any IO errors occur.
     * @throws SAXException                 If any parse errors occur.
     */
    public static XMLDocument parse(String str) throws ParserConfigurationException, IOException, SAXException {

        DocumentBuilderFactory docBuilderFactory = getSecuredDocumentBuilder();
        DocumentBuilder builder = docBuilderFactory.newDocumentBuilder();
        try (InputStream inputStream = new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8))) {
            Document doc = builder.parse(inputStream);
            if (doc != null) {
                if (log.isDebugEnabled()) {
                    log.debug("Parsing to XMLDocument Success. Src string: " + str);
                }
                return new XMLDocument(doc);
            }
        }
        return null;
    }

    /**
     * Create DocumentBuilderFactory with the XXE and XEE prevention measurements.
     *
     * @return DocumentBuilderFactory instance
     */
    public static DocumentBuilderFactory getSecuredDocumentBuilder() {

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        dbf.setXIncludeAware(false);
        dbf.setExpandEntityReferences(false);
        try {
            dbf.setFeature(Constants.SAX_FEATURE_PREFIX + Constants.EXTERNAL_GENERAL_ENTITIES_FEATURE, false);
            dbf.setFeature(Constants.SAX_FEATURE_PREFIX + Constants.EXTERNAL_PARAMETER_ENTITIES_FEATURE, false);
            dbf.setFeature(Constants.XERCES_FEATURE_PREFIX + Constants.LOAD_EXTERNAL_DTD_FEATURE, false);
            dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);

        } catch (ParserConfigurationException e) {
            log.error("Failed to load XML Processor Feature " + Constants.EXTERNAL_GENERAL_ENTITIES_FEATURE + " or "
                    + Constants.EXTERNAL_PARAMETER_ENTITIES_FEATURE + " or " + Constants.LOAD_EXTERNAL_DTD_FEATURE
                    + " or secure-processing.");
        }

        SecurityManager securityManager = new SecurityManager();
        securityManager.setEntityExpansionLimit(ENTITY_EXPANSION_LIMIT);
        dbf.setAttribute(Constants.XERCES_PROPERTY_PREFIX + Constants.SECURITY_MANAGER_PROPERTY, securityManager);

        return dbf;

    }

    /**
     * Function to convert XMLDocument to String
     *
     * @param xmlDoc XMLDocument object to convert
     * @return xml in string form
     * @throws TransformerException
     */
    public static String stringify(XMLDocument xmlDoc) throws TransformerException {

        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        StringWriter strWriter = new StringWriter();

        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
        transformer.transform(new DOMSource(xmlDoc), new StreamResult(strWriter));

        if (log.isDebugEnabled()) {
            log.debug("XMLDocument to String : " + strWriter.getBuffer().toString());
        }

        return strWriter.getBuffer().toString();
    }

    public static Object evaluateXPath(Document doc, String xpathStr, QName returnType)
            throws XPathExpressionException {
        XPath xPath = XPathFactory.newInstance().newXPath();
        // Possible xpath injection attack. However this code is reached only when executing a bpmn process
        // BPMN process implementor should know the security risk and should evaluate only valid measures
        return xPath.evaluate(xpathStr, doc, returnType);
    }

    /**
     * Function to evaluate xpath. This will resolve the NodeList to Node if the result contains only one node
     * @param doc
     * @param xpathStr
     * @return
     * @throws XPathExpressionException
     */
    public static Object evaluateXPath(Document doc, String xpathStr) throws BPMNXmlException {

        Object result = null;
        NodeList outputObjList = null;
        try {
            outputObjList = (NodeList) evaluateXPath(doc, xpathStr, XPathConstants.NODESET);
            if (outputObjList.getLength() == 1) {
                //If there is only one node
                if (outputObjList.item(0) instanceof Text) {
                    return ((Text) outputObjList.item(0)).getWholeText();
                }
                return outputObjList.item(0);
            }
            return outputObjList;

        } catch (XPathExpressionException eLevel1) {
            //provided xpath cannot be evaluated to NodeList, so it may be evaluated to string
            try {

                if (log.isDebugEnabled()) {
                    log.debug("Since evaluating the xpath: " + xpathStr
                            + " to NodeList failed, retrying to evaluate it to a STRING");
                }
                return evaluateXPath(doc, xpathStr, XPathConstants.STRING);

            } catch (XPathExpressionException eLevel2) {
                if (log.isDebugEnabled()) {
                    log.debug("Error occurred while evaluating xpath :" + xpathStr + " on xml: " + doc.toString());
                }
                throw new BPMNXmlException(
                        "Error occurred while evaluating xpath :" + xpathStr + " due to error in xpath", eLevel2);
            }
        }
    }
}