VYPR
High severityNVD Advisory· Published Mar 20, 2018· Updated Sep 16, 2024

CVE-2018-1321

CVE-2018-1321

Description

Apache Syncope allows authenticated administrators to execute arbitrary code or access file system resources via insecure XSLT processing.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Apache Syncope allows authenticated administrators to execute arbitrary code or access file system resources via insecure XSLT processing.

Vulnerability

An insecure XSLT processing vulnerability exists in Apache Syncope versions 1.2.x before 1.2.11, 2.0.x before 2.0.8, and unsupported releases 1.0.x and 1.1.x which may also be affected [2][3]. An administrator with report and template entitlements can supply malicious XSL Transformations (XSLT) that are processed without proper restrictions, leading to arbitrary file read, file write, and code execution [1][4]. The flaw resides in components that handle XSLT transformations across the platform.

Exploitation

An attacker must be an authenticated administrator who possesses report and template entitlements in Apache Syncope [3]. No additional privileges or user interaction beyond the attacker's own actions are required. The attacker crafts a malicious XSLT stylesheet that includes operations such as reading or writing files on the server filesystem, or executing system commands via Java extension functions. When the Syncope engine processes the malicious XSLT, it executes the attacker-supplied transformations in the context of the application server.

Impact

Successful exploitation allows an attacker to read arbitrary files from the server (information disclosure), write arbitrary files (potential system compromise or backdoor), and execute arbitrary code on the server [2][3]. This leads to complete confidentiality, integrity, and availability compromise of the affected Apache Syncope instance.

Mitigation

Upgrade to Apache Syncope 1.2.11, 2.0.8, or later versions as these releases include secure XSLT processing fixes identified in commits [ad31479] and [726231f] [1][4]. Users still running unsupported releases (1.0.x, 1.1.x) should upgrade immediately to a supported patched version [2]. No workaround is available; the only mitigation is to apply the patch or upgrade. This CVE is not listed in the Known Exploited Vulnerabilities (KEV) catalog.

AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.apache.syncope:syncope-coreMaven
< 1.2.111.2.11
org.apache.syncope:syncope-coreMaven
>= 2.0.0, < 2.0.82.0.8

Affected products

2

Patches

4
726231fbf7b8

Ensuring secure XSLT processing everywhere

https://github.com/apache/syncopeFrancesco ChicchiriccòMar 8, 2018via ghsa
6 files changed · +268 14
  • console/src/main/java/org/apache/syncope/console/commons/XMLRolesReader.java+2 6 modified
    @@ -20,11 +20,10 @@
     
     import java.util.HashMap;
     import java.util.Map;
    -import javax.xml.parsers.DocumentBuilder;
    -import javax.xml.parsers.DocumentBuilderFactory;
     import org.apache.commons.lang3.StringUtils;
     import org.apache.commons.lang3.tuple.ImmutablePair;
     import org.apache.commons.lang3.tuple.Pair;
    +import org.apache.cxf.staxutils.StaxUtils;
     import org.slf4j.Logger;
     import org.slf4j.LoggerFactory;
     import org.w3c.dom.Document;
    @@ -52,11 +51,8 @@ public void setAuthorizations(final String authorizations) {
         private void init() {
             authMap = new HashMap<Pair<String, String>, String>();
     
    -        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    -        dbf.setNamespaceAware(true);
             try {
    -            DocumentBuilder db = dbf.newDocumentBuilder();
    -            Document doc = db.parse(getClass().getResource("/" + authorizations).openStream());
    +            Document doc = StaxUtils.read(getClass().getResource("/" + authorizations).openStream());
                 doc.getDocumentElement().normalize();
     
                 Node authNode = null;
    
  • core/src/main/java/org/apache/syncope/core/report/ReportJob.java+18 3 modified
    @@ -24,8 +24,11 @@
     import java.util.zip.Deflater;
     import java.util.zip.ZipEntry;
     import java.util.zip.ZipOutputStream;
    +import javax.xml.XMLConstants;
     import javax.xml.transform.OutputKeys;
     import javax.xml.transform.Transformer;
    +import javax.xml.transform.TransformerConfigurationException;
    +import javax.xml.transform.TransformerFactory;
     import javax.xml.transform.sax.SAXTransformerFactory;
     import javax.xml.transform.sax.TransformerHandler;
     import javax.xml.transform.stream.StreamResult;
    @@ -40,6 +43,7 @@
     import org.apache.syncope.core.rest.data.ReportDataBinder;
     import org.apache.syncope.core.util.ApplicationContextProvider;
     import org.apache.syncope.core.util.ExceptionUtil;
    +import org.apache.syncope.core.util.VoidURIResolver;
     import org.quartz.DisallowConcurrentExecution;
     import org.quartz.Job;
     import org.quartz.JobExecutionContext;
    @@ -62,6 +66,18 @@ public class ReportJob implements Job {
          */
         private static final Logger LOG = LoggerFactory.getLogger(ReportJob.class);
     
    +    private static final SAXTransformerFactory TRANSFORMER_FACTORY;
    +
    +    static {
    +        TRANSFORMER_FACTORY = (SAXTransformerFactory) TransformerFactory.newInstance();
    +        TRANSFORMER_FACTORY.setURIResolver(new VoidURIResolver());
    +        try {
    +            TRANSFORMER_FACTORY.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
    +        } catch (TransformerConfigurationException e) {
    +            LOG.error("Could not enable secure XML processing", e);
    +        }
    +    }
    +
         /**
          * Report DAO.
          */
    @@ -119,8 +135,7 @@ public void execute(final JobExecutionContext context) throws JobExecutionExcept
             ZipOutputStream zos = new ZipOutputStream(baos);
             zos.setLevel(Deflater.BEST_COMPRESSION);
             try {
    -            SAXTransformerFactory tFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
    -            handler = tFactory.newTransformerHandler();
    +            handler = TRANSFORMER_FACTORY.newTransformerHandler();
                 Transformer serializer = handler.getTransformer();
                 serializer.setOutputProperty(OutputKeys.ENCODING, SyncopeConstants.DEFAULT_ENCODING);
                 serializer.setOutputProperty(OutputKeys.INDENT, "yes");
    @@ -153,7 +168,7 @@ public void execute(final JobExecutionContext context) throws JobExecutionExcept
                     if (reportletClass != null) {
                         Reportlet<ReportletConf> autowired =
                                 (Reportlet<ReportletConf>) ApplicationContextProvider.getBeanFactory().
    -                            createBean(reportletClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
    +                                    createBean(reportletClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
                         autowired.setConf(reportletConf);
     
                         // invoke reportlet
    
  • core/src/main/java/org/apache/syncope/core/rest/controller/ReportController.java+9 5 modified
    @@ -29,13 +29,13 @@
     import java.util.Map;
     import java.util.Set;
     import java.util.zip.ZipInputStream;
    +import javax.xml.transform.stream.StreamSource;
     import org.apache.cocoon.optional.pipeline.components.sax.fop.FopSerializer;
     import org.apache.cocoon.pipeline.NonCachingPipeline;
     import org.apache.cocoon.pipeline.Pipeline;
     import org.apache.cocoon.sax.SAXPipelineComponent;
     import org.apache.cocoon.sax.component.XMLGenerator;
     import org.apache.cocoon.sax.component.XMLSerializer;
    -import org.apache.cocoon.sax.component.XSLTTransformer;
     import org.apache.commons.io.IOUtils;
     import org.apache.commons.lang3.ArrayUtils;
     import org.apache.syncope.common.report.ReportletConf;
    @@ -195,28 +195,32 @@ public void exportExecutionResult(final OutputStream os, final ReportExec report
     
                 switch (format) {
                     case HTML:
    -                    XSLTTransformer xsl2html = new XSLTTransformer(getClass().getResource("/report/report2html.xsl"));
    +                    XSLTTransformer xsl2html = new XSLTTransformer(
    +                            new StreamSource(getClass().getResourceAsStream("/report/report2html.xsl")));
                         xsl2html.setParameters(parameters);
                         pipeline.addComponent(xsl2html);
                         pipeline.addComponent(XMLSerializer.createXHTMLSerializer());
                         break;
     
                     case PDF:
    -                    XSLTTransformer xsl2pdf = new XSLTTransformer(getClass().getResource("/report/report2fo.xsl"));
    +                    XSLTTransformer xsl2pdf = new XSLTTransformer(
    +                            new StreamSource(getClass().getResourceAsStream("/report/report2fo.xsl")));
                         xsl2pdf.setParameters(parameters);
                         pipeline.addComponent(xsl2pdf);
                         pipeline.addComponent(new FopSerializer(MimeConstants.MIME_PDF));
                         break;
     
                     case RTF:
    -                    XSLTTransformer xsl2rtf = new XSLTTransformer(getClass().getResource("/report/report2fo.xsl"));
    +                    XSLTTransformer xsl2rtf = new XSLTTransformer(
    +                            new StreamSource(getClass().getResourceAsStream("/report/report2fo.xsl")));
                         xsl2rtf.setParameters(parameters);
                         pipeline.addComponent(xsl2rtf);
                         pipeline.addComponent(new FopSerializer(MimeConstants.MIME_RTF));
                         break;
     
                     case CSV:
    -                    XSLTTransformer xsl2csv = new XSLTTransformer(getClass().getResource("/report/report2csv.xsl"));
    +                    XSLTTransformer xsl2csv = new XSLTTransformer(
    +                            new StreamSource(getClass().getResourceAsStream("/report/report2csv.xsl")));
                         xsl2csv.setParameters(parameters);
                         pipeline.addComponent(xsl2csv);
                         pipeline.addComponent(new TextSerializer());
    
  • core/src/main/java/org/apache/syncope/core/rest/controller/XSLTTransformer.java+201 0 added
    @@ -0,0 +1,201 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF 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.apache.syncope.core.rest.controller;
    +
    +import java.util.HashMap;
    +import java.util.Map;
    +import java.util.regex.Pattern;
    +import javax.xml.XMLConstants;
    +import javax.xml.transform.Source;
    +import javax.xml.transform.Templates;
    +import javax.xml.transform.Transformer;
    +import javax.xml.transform.TransformerConfigurationException;
    +import javax.xml.transform.sax.SAXResult;
    +import javax.xml.transform.sax.SAXTransformerFactory;
    +import javax.xml.transform.sax.TransformerHandler;
    +import org.apache.cocoon.pipeline.SetupException;
    +import org.apache.cocoon.pipeline.caching.CacheKey;
    +import org.apache.cocoon.pipeline.component.CachingPipelineComponent;
    +import org.apache.cocoon.pipeline.util.StringRepresentation;
    +import org.apache.cocoon.sax.AbstractSAXTransformer;
    +import org.apache.cocoon.sax.SAXConsumer;
    +import org.apache.cocoon.sax.util.SAXConsumerAdapter;
    +import org.apache.syncope.core.util.VoidURIResolver;
    +import org.slf4j.Logger;
    +import org.slf4j.LoggerFactory;
    +
    +public class XSLTTransformer extends AbstractSAXTransformer implements CachingPipelineComponent {
    +
    +    private static final Logger LOG = LoggerFactory.getLogger(XSLTTransformer.class);
    +
    +    /**
    +     * A generic transformer factory to parse XSLTs.
    +     */
    +    private static final SAXTransformerFactory TRAX_FACTORY = createNewSAXTransformerFactory();
    +
    +    /**
    +     * The XSLT parameters name pattern.
    +     */
    +    private static final Pattern XSLT_PARAMETER_NAME_PATTERN = Pattern.compile("[a-zA-Z_][\\w\\-\\.]*");
    +
    +    /**
    +     * The XSLT parameters reference.
    +     */
    +    private Map<String, Object> parameters;
    +
    +    /**
    +     * The XSLT Template reference.
    +     */
    +    private Templates templates;
    +
    +    private Source source;
    +
    +    public XSLTTransformer(final Source source) {
    +        super();
    +        this.load(source, null);
    +    }
    +
    +    /**
    +     * Creates a new transformer reading the XSLT from the Source source and setting the TransformerFactory attributes.
    +     *
    +     * This constructor is useful when users want to perform XSLT transformation using <a
    +     * href="http://xml.apache.org/xalan-j/xsltc_usage.html">xsltc</a>.
    +     *
    +     * @param source the XSLT source
    +     * @param attributes the Transformer Factory attributes
    +     */
    +    public XSLTTransformer(final Source source, final Map<String, Object> attributes) {
    +        super();
    +        this.load(source, attributes);
    +    }
    +
    +    /**
    +     * Method useful to create a new transformer reading the XSLT from the URL source and setting the Transformer
    +     * Factory attributes.
    +     *
    +     * This method is useful when users want to perform XSLT transformation using <a
    +     * href="http://xml.apache.org/xalan-j/xsltc_usage.html">xsltc</a>.
    +     *
    +     * @param source the XSLT source
    +     * @param attributes the Transformer Factory attributes
    +     */
    +    private void load(final Source source, final Map<String, Object> attributes) {
    +        if (source == null) {
    +            throw new IllegalArgumentException("The parameter 'source' mustn't be null.");
    +        }
    +
    +        this.source = source;
    +
    +        this.load(this.source, this.source.toString(), attributes);
    +    }
    +
    +    private void load(final Source source, final String localCacheKey, final Map<String, Object> attributes) {
    +        LOG.debug("{} local cache miss: {}", getClass().getSimpleName(), localCacheKey);
    +
    +        // XSLT has to be parsed
    +        final SAXTransformerFactory transformerFactory;
    +        if (attributes == null || attributes.isEmpty()) {
    +            transformerFactory = TRAX_FACTORY;
    +        } else {
    +            transformerFactory = createNewSAXTransformerFactory();
    +            for (Map.Entry<String, Object> attribute : attributes.entrySet()) {
    +                transformerFactory.setAttribute(attribute.getKey(), attribute.getValue());
    +            }
    +        }
    +
    +        try {
    +            this.templates = transformerFactory.newTemplates(source);
    +        } catch (TransformerConfigurationException e) {
    +            throw new SetupException("Impossible to read XSLT from '" + source + "', see nested exception", e);
    +        }
    +    }
    +
    +    /**
    +     * Sets the XSLT parameters to be applied to XSLT stylesheet.
    +     *
    +     * @param parameters the XSLT parameters to be applied to XSLT stylesheet
    +     */
    +    public void setParameters(final Map<String, ? extends Object> parameters) {
    +        if (parameters == null) {
    +            this.parameters = null;
    +        } else {
    +            this.parameters = new HashMap<String, Object>(parameters);
    +        }
    +    }
    +
    +    /**
    +     * {@inheritDoc}
    +     */
    +    @Override
    +    protected void setSAXConsumer(final SAXConsumer consumer) {
    +        TransformerHandler transformerHandler;
    +        try {
    +            transformerHandler = TRAX_FACTORY.newTransformerHandler(this.templates);
    +        } catch (Exception e) {
    +            throw new SetupException("Could not initialize transformer handler.", e);
    +        }
    +
    +        if (this.parameters != null) {
    +            final Transformer transformer = transformerHandler.getTransformer();
    +
    +            for (Map.Entry<String, Object> entry : this.parameters.entrySet()) {
    +                final String name = entry.getKey();
    +
    +                // is valid XSLT parameter name
    +                if (XSLT_PARAMETER_NAME_PATTERN.matcher(name).matches()) {
    +                    transformer.setParameter(name, entry.getValue());
    +                }
    +            }
    +        }
    +
    +        final SAXResult result = new SAXResult();
    +        result.setHandler(consumer);
    +        // According to TrAX specs, all TransformerHandlers are LexicalHandlers
    +        result.setLexicalHandler(consumer);
    +        transformerHandler.setResult(result);
    +
    +        final SAXConsumerAdapter saxConsumerAdapter = new SAXConsumerAdapter();
    +        saxConsumerAdapter.setContentHandler(transformerHandler);
    +        super.setSAXConsumer(saxConsumerAdapter);
    +    }
    +
    +    @Override
    +    public CacheKey constructCacheKey() {
    +        return null;
    +    }
    +
    +    /**
    +     * Utility method to create a new transformer factory.
    +     *
    +     * @return a new transformer factory
    +     */
    +    private static SAXTransformerFactory createNewSAXTransformerFactory() {
    +        SAXTransformerFactory transformerFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
    +        transformerFactory.setURIResolver(new VoidURIResolver());
    +        try {
    +            transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
    +        } catch (TransformerConfigurationException e) {
    +            LOG.error("Could not enable secure XML processing", e);
    +        }
    +        return transformerFactory;
    +    }
    +
    +    @Override
    +    public String toString() {
    +        return StringRepresentation.buildString(this, "src=<" + this.source + ">");
    +    }
    +}
    
  • core/src/main/java/org/apache/syncope/core/util/ContentExporter.java+3 0 modified
    @@ -40,6 +40,7 @@
     import java.util.Set;
     import java.util.TreeMap;
     import java.util.TreeSet;
    +import javax.xml.XMLConstants;
     import javax.xml.transform.OutputKeys;
     import javax.xml.transform.Transformer;
     import javax.xml.transform.TransformerConfigurationException;
    @@ -346,6 +347,8 @@ public void export(final OutputStream os, final String wfTablePrefix)
     
             StreamResult streamResult = new StreamResult(os);
             final SAXTransformerFactory transformerFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
    +        transformerFactory.setURIResolver(new VoidURIResolver());
    +        transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
     
             TransformerHandler handler = transformerFactory.newTransformerHandler();
             Transformer serializer = handler.getTransformer();
    
  • core/src/main/java/org/apache/syncope/core/util/VoidURIResolver.java+35 0 added
    @@ -0,0 +1,35 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one
    + * or more contributor license agreements.  See the NOTICE file
    + * distributed with this work for additional information
    + * regarding copyright ownership.  The ASF 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.apache.syncope.core.util;
    +
    +import javax.xml.transform.Source;
    +import javax.xml.transform.TransformerException;
    +import javax.xml.transform.URIResolver;
    +
    +/**
    + * This implementation disallows any XSLT include, for security reasons.
    + */
    +public class VoidURIResolver implements URIResolver {
    +
    +    @Override
    +    public Source resolve(final String href, final String base) throws TransformerException {
    +        return null;
    +    }
    +
    +}
    
726231fbf7b8

Ensuring secure XSLT processing everywhere

https://github.com/apache/syncopeFrancesco ChicchiriccòMar 8, 2018via ghsa
6 files changed · +268 14
  • console/src/main/java/org/apache/syncope/console/commons/XMLRolesReader.java+2 6 modified
    @@ -20,11 +20,10 @@
     
     import java.util.HashMap;
     import java.util.Map;
    -import javax.xml.parsers.DocumentBuilder;
    -import javax.xml.parsers.DocumentBuilderFactory;
     import org.apache.commons.lang3.StringUtils;
     import org.apache.commons.lang3.tuple.ImmutablePair;
     import org.apache.commons.lang3.tuple.Pair;
    +import org.apache.cxf.staxutils.StaxUtils;
     import org.slf4j.Logger;
     import org.slf4j.LoggerFactory;
     import org.w3c.dom.Document;
    @@ -52,11 +51,8 @@ public void setAuthorizations(final String authorizations) {
         private void init() {
             authMap = new HashMap<Pair<String, String>, String>();
     
    -        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    -        dbf.setNamespaceAware(true);
             try {
    -            DocumentBuilder db = dbf.newDocumentBuilder();
    -            Document doc = db.parse(getClass().getResource("/" + authorizations).openStream());
    +            Document doc = StaxUtils.read(getClass().getResource("/" + authorizations).openStream());
                 doc.getDocumentElement().normalize();
     
                 Node authNode = null;
    
  • core/src/main/java/org/apache/syncope/core/report/ReportJob.java+18 3 modified
    @@ -24,8 +24,11 @@
     import java.util.zip.Deflater;
     import java.util.zip.ZipEntry;
     import java.util.zip.ZipOutputStream;
    +import javax.xml.XMLConstants;
     import javax.xml.transform.OutputKeys;
     import javax.xml.transform.Transformer;
    +import javax.xml.transform.TransformerConfigurationException;
    +import javax.xml.transform.TransformerFactory;
     import javax.xml.transform.sax.SAXTransformerFactory;
     import javax.xml.transform.sax.TransformerHandler;
     import javax.xml.transform.stream.StreamResult;
    @@ -40,6 +43,7 @@
     import org.apache.syncope.core.rest.data.ReportDataBinder;
     import org.apache.syncope.core.util.ApplicationContextProvider;
     import org.apache.syncope.core.util.ExceptionUtil;
    +import org.apache.syncope.core.util.VoidURIResolver;
     import org.quartz.DisallowConcurrentExecution;
     import org.quartz.Job;
     import org.quartz.JobExecutionContext;
    @@ -62,6 +66,18 @@ public class ReportJob implements Job {
          */
         private static final Logger LOG = LoggerFactory.getLogger(ReportJob.class);
     
    +    private static final SAXTransformerFactory TRANSFORMER_FACTORY;
    +
    +    static {
    +        TRANSFORMER_FACTORY = (SAXTransformerFactory) TransformerFactory.newInstance();
    +        TRANSFORMER_FACTORY.setURIResolver(new VoidURIResolver());
    +        try {
    +            TRANSFORMER_FACTORY.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
    +        } catch (TransformerConfigurationException e) {
    +            LOG.error("Could not enable secure XML processing", e);
    +        }
    +    }
    +
         /**
          * Report DAO.
          */
    @@ -119,8 +135,7 @@ public void execute(final JobExecutionContext context) throws JobExecutionExcept
             ZipOutputStream zos = new ZipOutputStream(baos);
             zos.setLevel(Deflater.BEST_COMPRESSION);
             try {
    -            SAXTransformerFactory tFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
    -            handler = tFactory.newTransformerHandler();
    +            handler = TRANSFORMER_FACTORY.newTransformerHandler();
                 Transformer serializer = handler.getTransformer();
                 serializer.setOutputProperty(OutputKeys.ENCODING, SyncopeConstants.DEFAULT_ENCODING);
                 serializer.setOutputProperty(OutputKeys.INDENT, "yes");
    @@ -153,7 +168,7 @@ public void execute(final JobExecutionContext context) throws JobExecutionExcept
                     if (reportletClass != null) {
                         Reportlet<ReportletConf> autowired =
                                 (Reportlet<ReportletConf>) ApplicationContextProvider.getBeanFactory().
    -                            createBean(reportletClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
    +                                    createBean(reportletClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
                         autowired.setConf(reportletConf);
     
                         // invoke reportlet
    
  • core/src/main/java/org/apache/syncope/core/rest/controller/ReportController.java+9 5 modified
    @@ -29,13 +29,13 @@
     import java.util.Map;
     import java.util.Set;
     import java.util.zip.ZipInputStream;
    +import javax.xml.transform.stream.StreamSource;
     import org.apache.cocoon.optional.pipeline.components.sax.fop.FopSerializer;
     import org.apache.cocoon.pipeline.NonCachingPipeline;
     import org.apache.cocoon.pipeline.Pipeline;
     import org.apache.cocoon.sax.SAXPipelineComponent;
     import org.apache.cocoon.sax.component.XMLGenerator;
     import org.apache.cocoon.sax.component.XMLSerializer;
    -import org.apache.cocoon.sax.component.XSLTTransformer;
     import org.apache.commons.io.IOUtils;
     import org.apache.commons.lang3.ArrayUtils;
     import org.apache.syncope.common.report.ReportletConf;
    @@ -195,28 +195,32 @@ public void exportExecutionResult(final OutputStream os, final ReportExec report
     
                 switch (format) {
                     case HTML:
    -                    XSLTTransformer xsl2html = new XSLTTransformer(getClass().getResource("/report/report2html.xsl"));
    +                    XSLTTransformer xsl2html = new XSLTTransformer(
    +                            new StreamSource(getClass().getResourceAsStream("/report/report2html.xsl")));
                         xsl2html.setParameters(parameters);
                         pipeline.addComponent(xsl2html);
                         pipeline.addComponent(XMLSerializer.createXHTMLSerializer());
                         break;
     
                     case PDF:
    -                    XSLTTransformer xsl2pdf = new XSLTTransformer(getClass().getResource("/report/report2fo.xsl"));
    +                    XSLTTransformer xsl2pdf = new XSLTTransformer(
    +                            new StreamSource(getClass().getResourceAsStream("/report/report2fo.xsl")));
                         xsl2pdf.setParameters(parameters);
                         pipeline.addComponent(xsl2pdf);
                         pipeline.addComponent(new FopSerializer(MimeConstants.MIME_PDF));
                         break;
     
                     case RTF:
    -                    XSLTTransformer xsl2rtf = new XSLTTransformer(getClass().getResource("/report/report2fo.xsl"));
    +                    XSLTTransformer xsl2rtf = new XSLTTransformer(
    +                            new StreamSource(getClass().getResourceAsStream("/report/report2fo.xsl")));
                         xsl2rtf.setParameters(parameters);
                         pipeline.addComponent(xsl2rtf);
                         pipeline.addComponent(new FopSerializer(MimeConstants.MIME_RTF));
                         break;
     
                     case CSV:
    -                    XSLTTransformer xsl2csv = new XSLTTransformer(getClass().getResource("/report/report2csv.xsl"));
    +                    XSLTTransformer xsl2csv = new XSLTTransformer(
    +                            new StreamSource(getClass().getResourceAsStream("/report/report2csv.xsl")));
                         xsl2csv.setParameters(parameters);
                         pipeline.addComponent(xsl2csv);
                         pipeline.addComponent(new TextSerializer());
    
  • core/src/main/java/org/apache/syncope/core/rest/controller/XSLTTransformer.java+201 0 added
    @@ -0,0 +1,201 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF 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.apache.syncope.core.rest.controller;
    +
    +import java.util.HashMap;
    +import java.util.Map;
    +import java.util.regex.Pattern;
    +import javax.xml.XMLConstants;
    +import javax.xml.transform.Source;
    +import javax.xml.transform.Templates;
    +import javax.xml.transform.Transformer;
    +import javax.xml.transform.TransformerConfigurationException;
    +import javax.xml.transform.sax.SAXResult;
    +import javax.xml.transform.sax.SAXTransformerFactory;
    +import javax.xml.transform.sax.TransformerHandler;
    +import org.apache.cocoon.pipeline.SetupException;
    +import org.apache.cocoon.pipeline.caching.CacheKey;
    +import org.apache.cocoon.pipeline.component.CachingPipelineComponent;
    +import org.apache.cocoon.pipeline.util.StringRepresentation;
    +import org.apache.cocoon.sax.AbstractSAXTransformer;
    +import org.apache.cocoon.sax.SAXConsumer;
    +import org.apache.cocoon.sax.util.SAXConsumerAdapter;
    +import org.apache.syncope.core.util.VoidURIResolver;
    +import org.slf4j.Logger;
    +import org.slf4j.LoggerFactory;
    +
    +public class XSLTTransformer extends AbstractSAXTransformer implements CachingPipelineComponent {
    +
    +    private static final Logger LOG = LoggerFactory.getLogger(XSLTTransformer.class);
    +
    +    /**
    +     * A generic transformer factory to parse XSLTs.
    +     */
    +    private static final SAXTransformerFactory TRAX_FACTORY = createNewSAXTransformerFactory();
    +
    +    /**
    +     * The XSLT parameters name pattern.
    +     */
    +    private static final Pattern XSLT_PARAMETER_NAME_PATTERN = Pattern.compile("[a-zA-Z_][\\w\\-\\.]*");
    +
    +    /**
    +     * The XSLT parameters reference.
    +     */
    +    private Map<String, Object> parameters;
    +
    +    /**
    +     * The XSLT Template reference.
    +     */
    +    private Templates templates;
    +
    +    private Source source;
    +
    +    public XSLTTransformer(final Source source) {
    +        super();
    +        this.load(source, null);
    +    }
    +
    +    /**
    +     * Creates a new transformer reading the XSLT from the Source source and setting the TransformerFactory attributes.
    +     *
    +     * This constructor is useful when users want to perform XSLT transformation using <a
    +     * href="http://xml.apache.org/xalan-j/xsltc_usage.html">xsltc</a>.
    +     *
    +     * @param source the XSLT source
    +     * @param attributes the Transformer Factory attributes
    +     */
    +    public XSLTTransformer(final Source source, final Map<String, Object> attributes) {
    +        super();
    +        this.load(source, attributes);
    +    }
    +
    +    /**
    +     * Method useful to create a new transformer reading the XSLT from the URL source and setting the Transformer
    +     * Factory attributes.
    +     *
    +     * This method is useful when users want to perform XSLT transformation using <a
    +     * href="http://xml.apache.org/xalan-j/xsltc_usage.html">xsltc</a>.
    +     *
    +     * @param source the XSLT source
    +     * @param attributes the Transformer Factory attributes
    +     */
    +    private void load(final Source source, final Map<String, Object> attributes) {
    +        if (source == null) {
    +            throw new IllegalArgumentException("The parameter 'source' mustn't be null.");
    +        }
    +
    +        this.source = source;
    +
    +        this.load(this.source, this.source.toString(), attributes);
    +    }
    +
    +    private void load(final Source source, final String localCacheKey, final Map<String, Object> attributes) {
    +        LOG.debug("{} local cache miss: {}", getClass().getSimpleName(), localCacheKey);
    +
    +        // XSLT has to be parsed
    +        final SAXTransformerFactory transformerFactory;
    +        if (attributes == null || attributes.isEmpty()) {
    +            transformerFactory = TRAX_FACTORY;
    +        } else {
    +            transformerFactory = createNewSAXTransformerFactory();
    +            for (Map.Entry<String, Object> attribute : attributes.entrySet()) {
    +                transformerFactory.setAttribute(attribute.getKey(), attribute.getValue());
    +            }
    +        }
    +
    +        try {
    +            this.templates = transformerFactory.newTemplates(source);
    +        } catch (TransformerConfigurationException e) {
    +            throw new SetupException("Impossible to read XSLT from '" + source + "', see nested exception", e);
    +        }
    +    }
    +
    +    /**
    +     * Sets the XSLT parameters to be applied to XSLT stylesheet.
    +     *
    +     * @param parameters the XSLT parameters to be applied to XSLT stylesheet
    +     */
    +    public void setParameters(final Map<String, ? extends Object> parameters) {
    +        if (parameters == null) {
    +            this.parameters = null;
    +        } else {
    +            this.parameters = new HashMap<String, Object>(parameters);
    +        }
    +    }
    +
    +    /**
    +     * {@inheritDoc}
    +     */
    +    @Override
    +    protected void setSAXConsumer(final SAXConsumer consumer) {
    +        TransformerHandler transformerHandler;
    +        try {
    +            transformerHandler = TRAX_FACTORY.newTransformerHandler(this.templates);
    +        } catch (Exception e) {
    +            throw new SetupException("Could not initialize transformer handler.", e);
    +        }
    +
    +        if (this.parameters != null) {
    +            final Transformer transformer = transformerHandler.getTransformer();
    +
    +            for (Map.Entry<String, Object> entry : this.parameters.entrySet()) {
    +                final String name = entry.getKey();
    +
    +                // is valid XSLT parameter name
    +                if (XSLT_PARAMETER_NAME_PATTERN.matcher(name).matches()) {
    +                    transformer.setParameter(name, entry.getValue());
    +                }
    +            }
    +        }
    +
    +        final SAXResult result = new SAXResult();
    +        result.setHandler(consumer);
    +        // According to TrAX specs, all TransformerHandlers are LexicalHandlers
    +        result.setLexicalHandler(consumer);
    +        transformerHandler.setResult(result);
    +
    +        final SAXConsumerAdapter saxConsumerAdapter = new SAXConsumerAdapter();
    +        saxConsumerAdapter.setContentHandler(transformerHandler);
    +        super.setSAXConsumer(saxConsumerAdapter);
    +    }
    +
    +    @Override
    +    public CacheKey constructCacheKey() {
    +        return null;
    +    }
    +
    +    /**
    +     * Utility method to create a new transformer factory.
    +     *
    +     * @return a new transformer factory
    +     */
    +    private static SAXTransformerFactory createNewSAXTransformerFactory() {
    +        SAXTransformerFactory transformerFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
    +        transformerFactory.setURIResolver(new VoidURIResolver());
    +        try {
    +            transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
    +        } catch (TransformerConfigurationException e) {
    +            LOG.error("Could not enable secure XML processing", e);
    +        }
    +        return transformerFactory;
    +    }
    +
    +    @Override
    +    public String toString() {
    +        return StringRepresentation.buildString(this, "src=<" + this.source + ">");
    +    }
    +}
    
  • core/src/main/java/org/apache/syncope/core/util/ContentExporter.java+3 0 modified
    @@ -40,6 +40,7 @@
     import java.util.Set;
     import java.util.TreeMap;
     import java.util.TreeSet;
    +import javax.xml.XMLConstants;
     import javax.xml.transform.OutputKeys;
     import javax.xml.transform.Transformer;
     import javax.xml.transform.TransformerConfigurationException;
    @@ -346,6 +347,8 @@ public void export(final OutputStream os, final String wfTablePrefix)
     
             StreamResult streamResult = new StreamResult(os);
             final SAXTransformerFactory transformerFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
    +        transformerFactory.setURIResolver(new VoidURIResolver());
    +        transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
     
             TransformerHandler handler = transformerFactory.newTransformerHandler();
             Transformer serializer = handler.getTransformer();
    
  • core/src/main/java/org/apache/syncope/core/util/VoidURIResolver.java+35 0 added
    @@ -0,0 +1,35 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one
    + * or more contributor license agreements.  See the NOTICE file
    + * distributed with this work for additional information
    + * regarding copyright ownership.  The ASF 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.apache.syncope.core.util;
    +
    +import javax.xml.transform.Source;
    +import javax.xml.transform.TransformerException;
    +import javax.xml.transform.URIResolver;
    +
    +/**
    + * This implementation disallows any XSLT include, for security reasons.
    + */
    +public class VoidURIResolver implements URIResolver {
    +
    +    @Override
    +    public Source resolve(final String href, final String base) throws TransformerException {
    +        return null;
    +    }
    +
    +}
    
ad31479c1c54

Ensuring secure XSLT processing everywhere

https://github.com/apache/syncopeFrancesco ChicchiriccòMar 7, 2018via ghsa
16 files changed · +209 186
  • client/cli/src/main/java/org/apache/syncope/client/cli/commands/configuration/ConfigurationExport.java+3 21 modified
    @@ -18,18 +18,12 @@
      */
     package org.apache.syncope.client.cli.commands.configuration;
     
    -import java.io.FileNotFoundException;
    -import java.io.IOException;
     import java.io.InputStream;
    -import javax.xml.parsers.ParserConfigurationException;
    -import javax.xml.transform.TransformerConfigurationException;
    -import javax.xml.transform.TransformerException;
     import org.apache.syncope.client.cli.Input;
     import org.apache.syncope.client.cli.util.XMLUtils;
     import org.apache.syncope.common.lib.SyncopeClientException;
     import org.slf4j.Logger;
     import org.slf4j.LoggerFactory;
    -import org.xml.sax.SAXException;
     
     public class ConfigurationExport extends AbstractConfigurationCommand {
     
    @@ -53,24 +47,12 @@ public void export() {
                             input.firstParameter() + EXPORT_FILE_NAME);
                     configurationResultManager.genericMessage(
                             input.firstParameter() + EXPORT_FILE_NAME + " successfully created");
    -            } catch (final IOException ex) {
    -                LOG.error("Error exporting configuration", ex);
    -                configurationResultManager.genericError(ex.getMessage());
    -            } catch (ParserConfigurationException | SAXException | TransformerConfigurationException ex) {
    -                LOG.error("Error exporting configuration", ex);
    -                configurationResultManager.genericError(
    -                        "Error creating " + input.firstParameter() + EXPORT_FILE_NAME + " " + ex.getMessage());
    -            } catch (final TransformerException ex) {
    -                LOG.error("Error exporting configuration", ex);
    -                if (ex.getCause() instanceof FileNotFoundException) {
    -                    configurationResultManager.genericError("Permission denied on " + input.firstParameter());
    -                } else {
    -                    configurationResultManager.genericError(
    -                            "Error creating " + input.firstParameter() + EXPORT_FILE_NAME + " " + ex.getMessage());
    -                }
                 } catch (final SyncopeClientException ex) {
                     LOG.error("Error exporting configuration", ex);
                     configurationResultManager.genericError("Error calling configuration service " + ex.getMessage());
    +            } catch (final Exception ex) {
    +                LOG.error("Error exporting configuration", ex);
    +                configurationResultManager.genericError(ex.getMessage());
                 }
             } else {
                 configurationResultManager.commandOptionError(EXPORT_HELP_MESSAGE);
    
  • client/cli/src/main/java/org/apache/syncope/client/cli/commands/report/ReportExportExecution.java+8 23 modified
    @@ -18,25 +18,19 @@
      */
     package org.apache.syncope.client.cli.commands.report;
     
    -import java.io.IOException;
     import java.util.Arrays;
    -import javax.xml.parsers.ParserConfigurationException;
    -import javax.xml.transform.TransformerException;
     import javax.xml.ws.WebServiceException;
     import org.apache.syncope.client.cli.Input;
    -import org.apache.syncope.client.cli.util.CommandUtils;
     import org.apache.syncope.common.lib.SyncopeClientException;
    -import org.apache.syncope.common.lib.types.ReportExecExportFormat;
     import org.slf4j.Logger;
     import org.slf4j.LoggerFactory;
    -import org.xml.sax.SAXException;
     
     public class ReportExportExecution extends AbstractReportCommand {
     
         private static final Logger LOG = LoggerFactory.getLogger(ReportExportExecution.class);
     
    -    private static final String EXPORT_EXECUTION_HELP_MESSAGE
    -            = "report --export-execution-result {EXECUTION-KEY} {EXECUTION-KEY} [...] {FORMAT}\n"
    +    private static final String EXPORT_EXECUTION_HELP_MESSAGE =
    +            "report --export-execution-result {EXECUTION-KEY} {EXECUTION-KEY} [...] {FORMAT}\n"
                 + "          Format: CSV / HTML / PDF / XML / RTF";
     
         private final Input input;
    @@ -50,28 +44,19 @@ public void export() {
                 final String[] parameters = Arrays.copyOf(input.getParameters(), input.parameterNumber() - 1);
                 for (final String parameter : parameters) {
                     try {
    -                    final String result = reportSyncopeOperations.exportExecutionResult(
    -                            parameter, input.lastParameter());
    +                    String result = reportSyncopeOperations.exportExecutionResult(parameter, input.lastParameter());
                         reportResultManager.genericMessage(result + "created.");
    -                } catch (final WebServiceException | SyncopeClientException ex) {
    -                    LOG.error("Error exporting execution", ex);
    -                    if (ex.getMessage().startsWith("NotFound")) {
    +                } catch (final WebServiceException | SyncopeClientException e) {
    +                    LOG.error("Error exporting execution", e);
    +                    if (e.getMessage().startsWith("NotFound")) {
                             reportResultManager.notFoundError("Report", parameter);
                         } else {
    -                        reportResultManager.genericError(ex.getMessage());
    +                        reportResultManager.genericError(e.getMessage());
                         }
    -                } catch (final NumberFormatException ex) {
    -                    LOG.error("Error exporting execution", ex);
    -                    reportResultManager.numberFormatException("report", parameter);
    -                } catch (IOException | ParserConfigurationException | SAXException | TransformerException e) {
    +                } catch (final Exception e) {
                         LOG.error("Error exporting execution", e);
                         reportResultManager.genericError(
                                 " - Error creating " + "export_" + parameter + " " + e.getMessage());
    -                } catch (final IllegalArgumentException ex) {
    -                    LOG.error("Error exporting execution", ex);
    -                    reportResultManager.typeNotValidError(
    -                            "format", input.firstParameter(),
    -                            CommandUtils.fromEnumToArray(ReportExecExportFormat.class));
                     }
                     break;
                 }
    
  • client/cli/src/main/java/org/apache/syncope/client/cli/commands/report/ReportSyncopeOperations.java+1 5 modified
    @@ -18,14 +18,11 @@
      */
     package org.apache.syncope.client.cli.commands.report;
     
    -import java.io.IOException;
     import java.io.OutputStream;
     import java.io.SequenceInputStream;
     import java.nio.file.Files;
     import java.nio.file.Paths;
     import java.util.List;
    -import javax.xml.parsers.ParserConfigurationException;
    -import javax.xml.transform.TransformerException;
     import org.apache.cxf.helpers.IOUtils;
     import org.apache.syncope.client.cli.SyncopeServices;
     import org.apache.syncope.client.cli.util.XMLUtils;
    @@ -34,7 +31,6 @@
     import org.apache.syncope.common.lib.types.ReportExecExportFormat;
     import org.apache.syncope.common.rest.api.beans.ExecuteQuery;
     import org.apache.syncope.common.rest.api.service.ReportService;
    -import org.xml.sax.SAXException;
     
     public class ReportSyncopeOperations {
     
    @@ -53,7 +49,7 @@ public List<ReportTO> list() {
         }
     
         public String exportExecutionResult(final String executionKey, final String reportExecExportFormat)
    -            throws TransformerException, SAXException, IOException, ParserConfigurationException {
    +            throws Exception {
     
             ReportExecExportFormat format = ReportExecExportFormat.valueOf(reportExecExportFormat);
             SequenceInputStream report = (SequenceInputStream) reportService.exportExecutionResult(executionKey, format).
    
  • client/cli/src/main/java/org/apache/syncope/client/cli/util/XMLUtils.java+14 19 modified
    @@ -19,34 +19,29 @@
     package org.apache.syncope.client.cli.util;
     
     import java.io.File;
    -import java.io.IOException;
     import java.io.InputStream;
    -import java.io.StringReader;
    -import javax.xml.parsers.DocumentBuilderFactory;
    -import javax.xml.parsers.ParserConfigurationException;
    -import javax.xml.transform.TransformerException;
    +import javax.xml.XMLConstants;
     import javax.xml.transform.TransformerFactory;
     import javax.xml.transform.dom.DOMSource;
     import javax.xml.transform.stream.StreamResult;
    -import org.apache.cxf.helpers.IOUtils;
    -import org.xml.sax.InputSource;
    -import org.xml.sax.SAXException;
    +import org.w3c.dom.bootstrap.DOMImplementationRegistry;
    +import org.w3c.dom.ls.DOMImplementationLS;
    +import org.w3c.dom.ls.LSInput;
    +import org.w3c.dom.ls.LSParser;
     
     public final class XMLUtils {
     
    -    public static void createXMLFile(final InputStream sis, final String filePath)
    -            throws TransformerException, SAXException, IOException, ParserConfigurationException {
    -
    -        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    -        dbf.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
    +    public static void createXMLFile(final InputStream sis, final String filePath) throws Exception {
    +        DOMImplementationRegistry reg = DOMImplementationRegistry.newInstance();
    +        DOMImplementationLS domImpl = (DOMImplementationLS) reg.getDOMImplementation("LS");
    +        LSInput lsinput = domImpl.createLSInput();
    +        lsinput.setByteStream(sis);
    +        LSParser parser = domImpl.createLSParser(DOMImplementationLS.MODE_SYNCHRONOUS, null);
     
             TransformerFactory tf = TransformerFactory.newInstance();
    -        tf.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
    -        
    -        tf.newTransformer().
    -                transform(new DOMSource(dbf.newDocumentBuilder().
    -                        parse(new InputSource(new StringReader(IOUtils.toString(sis))))),
    -                        new StreamResult(new File(filePath)));
    +        tf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
    +
    +        tf.newTransformer().transform(new DOMSource(parser.parse(lsinput)), new StreamResult(new File(filePath)));
         }
     
         private XMLUtils() {
    
  • core/logic/src/main/java/org/apache/syncope/core/logic/cocoon/XSLTTransformer.java+11 3 modified
    @@ -19,11 +19,11 @@
     import java.util.HashMap;
     import java.util.Map;
     import java.util.regex.Pattern;
    +import javax.xml.XMLConstants;
     import javax.xml.transform.Source;
     import javax.xml.transform.Templates;
     import javax.xml.transform.Transformer;
     import javax.xml.transform.TransformerConfigurationException;
    -import javax.xml.transform.TransformerFactory;
     import javax.xml.transform.sax.SAXResult;
     import javax.xml.transform.sax.SAXTransformerFactory;
     import javax.xml.transform.sax.TransformerHandler;
    @@ -34,6 +34,7 @@
     import org.apache.cocoon.sax.AbstractSAXTransformer;
     import org.apache.cocoon.sax.SAXConsumer;
     import org.apache.cocoon.sax.util.SAXConsumerAdapter;
    +import org.apache.syncope.core.provisioning.java.utils.VoidURIResolver;
     import org.slf4j.Logger;
     import org.slf4j.LoggerFactory;
     
    @@ -132,7 +133,7 @@ public void setParameters(final Map<String, ? extends Object> parameters) {
             if (parameters == null) {
                 this.parameters = null;
             } else {
    -            this.parameters = new HashMap<String, Object>(parameters);
    +            this.parameters = new HashMap<>(parameters);
             }
         }
     
    @@ -183,7 +184,14 @@ public CacheKey constructCacheKey() {
          * @return a new transformer factory
          */
         private static SAXTransformerFactory createNewSAXTransformerFactory() {
    -        return (SAXTransformerFactory) TransformerFactory.newInstance();
    +        SAXTransformerFactory transformerFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
    +        transformerFactory.setURIResolver(new VoidURIResolver());
    +        try {
    +            transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
    +        } catch (TransformerConfigurationException e) {
    +            LOG.error("Could not enable secure XML processing", e);
    +        }
    +        return transformerFactory;
         }
     
         @Override
    
  • core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentExporter.java+3 2 modified
    @@ -42,6 +42,7 @@
     import java.util.TreeMap;
     import java.util.TreeSet;
     import javax.sql.DataSource;
    +import javax.xml.XMLConstants;
     import javax.xml.transform.OutputKeys;
     import javax.xml.transform.Transformer;
     import javax.xml.transform.TransformerConfigurationException;
    @@ -367,8 +368,8 @@ public void export(
             }
     
             StreamResult streamResult = new StreamResult(os);
    -        final SAXTransformerFactory transformerFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
    -        transformerFactory.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
    +        SAXTransformerFactory transformerFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
    +        transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
     
             TransformerHandler handler = transformerFactory.newTransformerHandler();
             Transformer serializer = handler.getTransformer();
    
  • core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/DefaultReportJobDelegate.java+17 3 modified
    @@ -27,8 +27,11 @@
     import java.util.zip.Deflater;
     import java.util.zip.ZipEntry;
     import java.util.zip.ZipOutputStream;
    +import javax.xml.XMLConstants;
     import javax.xml.transform.OutputKeys;
     import javax.xml.transform.Transformer;
    +import javax.xml.transform.TransformerConfigurationException;
    +import javax.xml.transform.TransformerFactory;
     import javax.xml.transform.sax.SAXTransformerFactory;
     import javax.xml.transform.sax.TransformerHandler;
     import javax.xml.transform.stream.StreamResult;
    @@ -44,6 +47,7 @@
     import org.apache.syncope.core.persistence.api.entity.Report;
     import org.apache.syncope.core.persistence.api.entity.ReportExec;
     import org.apache.syncope.core.provisioning.api.job.report.ReportJobDelegate;
    +import org.apache.syncope.core.provisioning.java.utils.VoidURIResolver;
     import org.quartz.JobExecutionException;
     import org.slf4j.Logger;
     import org.slf4j.LoggerFactory;
    @@ -58,6 +62,18 @@ public class DefaultReportJobDelegate implements ReportJobDelegate {
     
         private static final Logger LOG = LoggerFactory.getLogger(ReportJobDelegate.class);
     
    +    private static final SAXTransformerFactory TRANSFORMER_FACTORY;
    +
    +    static {
    +        TRANSFORMER_FACTORY = (SAXTransformerFactory) TransformerFactory.newInstance();
    +        TRANSFORMER_FACTORY.setURIResolver(new VoidURIResolver());
    +        try {
    +            TRANSFORMER_FACTORY.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
    +        } catch (TransformerConfigurationException e) {
    +            LOG.error("Could not enable secure XML processing", e);
    +        }
    +    }
    +
         /**
          * Report DAO.
          */
    @@ -127,9 +143,7 @@ public void execute(final String reportKey) throws JobExecutionException {
             ZipOutputStream zos = new ZipOutputStream(baos);
             zos.setLevel(Deflater.BEST_COMPRESSION);
             try {
    -            SAXTransformerFactory tFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
    -            tFactory.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
    -            handler = tFactory.newTransformerHandler();
    +            handler = TRANSFORMER_FACTORY.newTransformerHandler();
                 Transformer serializer = handler.getTransformer();
                 serializer.setOutputProperty(OutputKeys.ENCODING, StandardCharsets.UTF_8.name());
                 serializer.setOutputProperty(OutputKeys.INDENT, "yes");
    
  • core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/VoidURIResolver.java+35 0 added
    @@ -0,0 +1,35 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one
    + * or more contributor license agreements.  See the NOTICE file
    + * distributed with this work for additional information
    + * regarding copyright ownership.  The ASF 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.apache.syncope.core.provisioning.java.utils;
    +
    +import javax.xml.transform.Source;
    +import javax.xml.transform.TransformerException;
    +import javax.xml.transform.URIResolver;
    +
    +/**
    + * This implementation disallows any XSLT include, for security reasons.
    + */
    +public class VoidURIResolver implements URIResolver {
    +
    +    @Override
    +    public Source resolve(final String href, final String base) throws TransformerException {
    +        return null;
    +    }
    +
    +}
    
  • ext/camel/logic/src/main/java/org/apache/syncope/core/logic/init/CamelRouteLoader.java+7 7 modified
    @@ -21,22 +21,23 @@
     import java.io.StringWriter;
     import java.util.Map;
     import javax.sql.DataSource;
    -import javax.xml.parsers.DocumentBuilder;
    -import javax.xml.parsers.DocumentBuilderFactory;
    +import javax.xml.XMLConstants;
     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 org.apache.commons.lang3.StringUtils;
    +import org.apache.cxf.staxutils.StaxUtils;
     import org.apache.syncope.common.lib.types.AnyTypeKind;
     import org.apache.syncope.common.lib.types.CamelEntitlement;
     import org.apache.syncope.core.provisioning.api.EntitlementsHolder;
     import org.apache.syncope.core.spring.ResourceWithFallbackLoader;
     import org.apache.syncope.core.persistence.api.DomainsHolder;
     import org.apache.syncope.core.persistence.api.SyncopeLoader;
     import org.apache.syncope.core.persistence.api.entity.CamelRoute;
    +import org.apache.syncope.core.provisioning.java.utils.VoidURIResolver;
     import org.slf4j.Logger;
     import org.slf4j.LoggerFactory;
     import org.springframework.beans.factory.annotation.Autowired;
    @@ -153,11 +154,10 @@ private void loadRoutes(
                     NodeList routeNodes;
                     if (IS_JBOSS) {
                         tf = TransformerFactory.newInstance();
    -                    tf.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
    -                    DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
    -                    dbFactory.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
    -                    DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
    -                    Document doc = dBuilder.parse(resource.getInputStream());
    +                    tf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
    +                    tf.setURIResolver(new VoidURIResolver());
    +
    +                    Document doc = StaxUtils.read(resource.getInputStream());
     
                         routeNodes = doc.getDocumentElement().getElementsByTagName("route");
                     } else {
    
  • ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2ReaderWriter.java+4 2 modified
    @@ -50,6 +50,7 @@
     import org.apache.syncope.common.lib.types.SAML2BindingType;
     import org.apache.syncope.common.lib.types.SignatureAlgorithm;
     import org.apache.syncope.core.logic.init.SAML2SPLoader;
    +import org.apache.syncope.core.provisioning.java.utils.VoidURIResolver;
     import org.apache.wss4j.common.crypto.Merlin;
     import org.apache.wss4j.common.ext.WSSecurityException;
     import org.apache.wss4j.common.saml.OpenSAMLUtil;
    @@ -77,6 +78,7 @@ public class SAML2ReaderWriter {
     
         static {
             TRANSFORMER_FACTORY = TransformerFactory.newInstance();
    +        TRANSFORMER_FACTORY.setURIResolver(new VoidURIResolver());
             try {
                 TRANSFORMER_FACTORY.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
             } catch (TransformerConfigurationException e) {
    @@ -103,14 +105,14 @@ public void init() {
             // Try to load a signature algorithm
             if (loader.getSignatureAlgorithm() != null) {
                 SignatureAlgorithm loadedSignatureAlgorithm =
    -                SignatureAlgorithm.valueOf(loader.getSignatureAlgorithm());
    +                    SignatureAlgorithm.valueOf(loader.getSignatureAlgorithm());
                 if (loadedSignatureAlgorithm != null) {
                     sigAlgo = loadedSignatureAlgorithm.getAlgorithm();
                     jceSigAlgo = JCEMapper.translateURItoJCEID(sigAlgo);
                 }
                 if (jceSigAlgo == null) {
                     LOG.warn("Signature algorithm {} is not valid. Using default algorithm instead.",
    -                         loader.getSignatureAlgorithm());
    +                        loader.getSignatureAlgorithm());
                     sigAlgo = null;
                 }
             }
    
  • fit/core-reference/src/test/java/org/apache/syncope/fit/core/ReportITCase.java+38 33 modified
    @@ -54,6 +54,40 @@
     
     public class ReportITCase extends AbstractITCase {
     
    +    protected static String execReport(final String reportKey) {
    +        ReportTO reportTO = reportService.read(reportKey);
    +        assertNotNull(reportTO);
    +        assertNotNull(reportTO.getExecutions());
    +
    +        int preExecSize = reportTO.getExecutions().size();
    +        ExecTO exec = reportService.execute(new ExecuteQuery.Builder().key(reportKey).build());
    +        assertNotNull(exec);
    +
    +        int i = 0;
    +        int maxit = 50;
    +
    +        // wait for completion (executions incremented)
    +        do {
    +            try {
    +                Thread.sleep(1000);
    +            } catch (InterruptedException e) {
    +            }
    +
    +            reportTO = reportService.read(reportKey);
    +
    +            assertNotNull(reportTO);
    +            assertNotNull(reportTO.getExecutions());
    +
    +            i++;
    +        } while (preExecSize == reportTO.getExecutions().size() && i < maxit);
    +        if (i == maxit) {
    +            fail("Timeout when executing report " + reportKey);
    +        }
    +        exec = reportTO.getExecutions().get(reportTO.getExecutions().size() - 1);
    +        assertEquals(ReportExecStatus.SUCCESS.name(), exec.getStatus());
    +        return exec.getKey();
    +    }
    +
         @Test
         public void getReportletConfs() {
             Set<String> reportletConfs = syncopeService.platform().getReportletConfs();
    @@ -139,35 +173,6 @@ public void delete() {
             }
         }
     
    -    private String execute(final String reportKey) {
    -        ExecTO execution = reportService.execute(new ExecuteQuery.Builder().key(reportKey).build());
    -        assertNotNull(execution);
    -
    -        int i = 0;
    -        int maxit = 50;
    -
    -        ReportTO reportTO;
    -
    -        // wait for report execution completion (executions incremented)
    -        do {
    -            try {
    -                Thread.sleep(1000);
    -            } catch (InterruptedException e) {
    -            }
    -
    -            reportTO = reportService.read(reportKey);
    -
    -            assertNotNull(reportTO);
    -            assertNotNull(reportTO.getExecutions());
    -
    -            i++;
    -        } while (reportTO.getExecutions().isEmpty()
    -                || (!ReportExecStatus.SUCCESS.name().equals(reportTO.getExecutions().get(0).getStatus()) && i < maxit));
    -        assertEquals(ReportExecStatus.SUCCESS.name(), reportTO.getExecutions().get(0).getStatus());
    -
    -        return reportTO.getExecutions().get(0).getKey();
    -    }
    -
         private void checkExport(final String execKey, final ReportExecExportFormat fmt) throws IOException {
             Response response = reportService.exportExecutionResult(execKey, fmt);
             assertNotNull(response);
    @@ -192,7 +197,7 @@ public void executeAndExport() throws IOException {
             assertNotNull(reportTO);
     
             try {
    -            execute(reportTO.getKey());
    +            execReport(reportTO.getKey());
                 fail();
             } catch (SyncopeClientException e) {
                 assertEquals(ClientExceptionType.Scheduling, e.getType());
    @@ -202,7 +207,7 @@ public void executeAndExport() throws IOException {
             reportTO.setActive(true);
             reportService.update(reportTO);
     
    -        String execKey = execute(reportTO.getKey());
    +        String execKey = execReport(reportTO.getKey());
     
             checkExport(execKey, ReportExecExportFormat.XML);
             checkExport(execKey, ReportExecExportFormat.HTML);
    @@ -226,7 +231,7 @@ public void deleteExecutions() {
             reportTO = createReport(reportTO);
             assertNotNull(reportTO);
     
    -        String execKey = execute(reportTO.getKey());
    +        String execKey = execReport(reportTO.getKey());
             assertNotNull(execKey);
     
             try {
    @@ -266,7 +271,7 @@ public void auditReport() throws IOException {
                 report.setTemplate("sample");
                 report = createReport(report);
     
    -            String execKey = execute(report.getKey());
    +            String execKey = execReport(report.getKey());
                 checkExport(execKey, ReportExecExportFormat.XML);
     
                 report = reportService.read(report.getKey());
    
  • fit/core-reference/src/test/java/org/apache/syncope/fit/core/ReportTemplateITCase.java+36 0 modified
    @@ -34,6 +34,7 @@
     import org.apache.syncope.common.lib.SyncopeClientException;
     import org.apache.syncope.common.lib.to.ReportTemplateTO;
     import org.apache.syncope.common.lib.types.ClientExceptionType;
    +import org.apache.syncope.common.lib.types.ReportExecExportFormat;
     import org.apache.syncope.common.lib.types.ReportTemplateFormat;
     import org.apache.syncope.fit.AbstractITCase;
     import org.junit.Test;
    @@ -150,6 +151,41 @@ public void crud() throws IOException {
             }
         }
     
    +    @Test
    +    public void safeTemplate() throws IOException {
    +        Response response = reportTemplateService.getFormat("sample", ReportTemplateFormat.HTML);
    +        String before = IOUtils.toString((InputStream) response.getEntity(), StandardCharsets.UTF_8);
    +        assertNotNull(before);
    +
    +        String execKey = ReportITCase.execReport("0062ea9c-924d-4ecf-9961-4492a8cc6d1b");
    +        assertNotNull(execKey);
    +        response = reportService.exportExecutionResult(execKey, ReportExecExportFormat.HTML);
    +        String result = IOUtils.toString((InputStream) response.getEntity(), StandardCharsets.UTF_8);
    +        assertNotNull(result);
    +        assertTrue(result.startsWith("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
    +                + "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" "
    +                + "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"));
    +
    +        String malicious = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
    +                + "<!DOCTYPE xsl:stylesheet "
    +                + "[<!ENTITY file SYSTEM \"webapps/syncope/WEB-INF/classes/security.properties\">]>\n"
    +                + "<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n"
    +                + "    <xsl:template match=\"/\">&file;</xsl:template>\n"
    +                + "</xsl:stylesheet>";
    +        try {
    +            reportTemplateService.setFormat("sample", ReportTemplateFormat.HTML,
    +                    IOUtils.toInputStream(malicious, StandardCharsets.UTF_8));
    +
    +            response = reportService.exportExecutionResult(execKey, ReportExecExportFormat.HTML);
    +            result = IOUtils.toString((InputStream) response.getEntity(), StandardCharsets.UTF_8);
    +            assertNotNull(result);
    +            assertTrue(result.isEmpty());
    +        } finally {
    +            reportTemplateService.setFormat("sample", ReportTemplateFormat.HTML,
    +                    IOUtils.toInputStream(before, StandardCharsets.UTF_8));
    +        }
    +    }
    +
         @Test
         public void issueSYNCOPE866() {
             ReportTemplateTO reportTemplateTO = new ReportTemplateTO();
    
  • installer/src/main/java/org/apache/syncope/installer/processes/ArchetypeProcess.java+8 27 modified
    @@ -21,16 +21,12 @@
     import org.apache.syncope.installer.utilities.FileSystemUtils;
     import com.izforge.izpack.panels.process.AbstractUIProcessHandler;
     import java.io.File;
    -import java.io.IOException;
     import java.util.Properties;
    -import javax.xml.parsers.ParserConfigurationException;
    -import javax.xml.transform.TransformerException;
     import org.apache.syncope.installer.files.ConsolePom;
     import org.apache.syncope.installer.files.CorePom;
     import org.apache.syncope.installer.files.ParentPom;
     import org.apache.syncope.installer.utilities.InstallLog;
     import org.apache.syncope.installer.utilities.MavenUtils;
    -import org.xml.sax.SAXException;
     
     public class ArchetypeProcess extends BaseProcess {
     
    @@ -65,30 +61,15 @@ public void run(final AbstractUIProcessHandler handler, final String[] args) {
             InstallLog.initialize(installPath, handler);
             MavenUtils mavenUtils = new MavenUtils(mavenDir, handler);
             File customMavenProxySettings = null;
    -        try {
    -            if (isProxyEnabled && mavenProxyAutoconf) {
    -                customMavenProxySettings = MavenUtils.createSettingsWithProxy(installPath, proxyHost, proxyPort,
    -                        proxyUser, proxyPwd);
    +        if (isProxyEnabled && mavenProxyAutoconf) {
    +            try {
    +                customMavenProxySettings =
    +                        MavenUtils.createSettingsWithProxy(installPath, proxyHost, proxyPort, proxyUser, proxyPwd);
    +            } catch (Exception e) {
    +                StringBuilder message = new StringBuilder("Error during creation of custom Maven settings.xml");
    +                handler.emitError(message.toString(), e.getMessage());
    +                InstallLog.getInstance().error(message.append('\n').append(e.getMessage()).toString());
                 }
    -        } catch (IOException e) {
    -            StringBuilder message = new StringBuilder("I/O error during creation of Maven custom settings.xml");
    -            handler.emitError(message.toString(), e.getMessage());
    -            InstallLog.getInstance().error(message.append('\n').append(e.getMessage()).toString());
    -        } catch (ParserConfigurationException e) {
    -            StringBuilder message = new StringBuilder(
    -                    "Parser configuration error during creation of Maven custom settings.xml");
    -            handler.emitError(message.toString(), e.getMessage());
    -            InstallLog.getInstance().error(message.append('\n').append(e.getMessage()).toString());
    -        } catch (TransformerException e) {
    -            StringBuilder message = new StringBuilder(
    -                    "Transformer error during creation of Maven custom settings.xml");
    -            handler.emitError(message.toString(), e.getMessage());
    -            InstallLog.getInstance().error(message.append('\n').append(e.getMessage()).toString());
    -        } catch (SAXException e) {
    -            StringBuilder message = new StringBuilder(
    -                    "XML parsing error during creation of Maven custom settings.xml");
    -            handler.emitError(message.toString(), e.getMessage());
    -            InstallLog.getInstance().error(message.append('\n').append(e.getMessage()).toString());
             }
     
             handler.logOutput("########################## IMPORTANT ##########################", true);
    
  • installer/src/main/java/org/apache/syncope/installer/processes/ContainerProcess.java+7 25 modified
    @@ -21,10 +21,7 @@
     import org.apache.syncope.installer.utilities.FileSystemUtils;
     import com.izforge.izpack.panels.process.AbstractUIProcessHandler;
     import java.io.File;
    -import java.io.IOException;
     import java.util.Properties;
    -import javax.xml.parsers.ParserConfigurationException;
    -import javax.xml.transform.TransformerException;
     import org.apache.syncope.installer.containers.Glassfish;
     import org.apache.syncope.installer.containers.Tomcat;
     import org.apache.syncope.installer.containers.jboss.JBoss;
    @@ -35,7 +32,6 @@
     import org.apache.syncope.installer.files.MasterDomainXml;
     import org.apache.syncope.installer.utilities.InstallLog;
     import org.apache.syncope.installer.utilities.MavenUtils;
    -import org.xml.sax.SAXException;
     
     public final class ContainerProcess extends BaseProcess {
     
    @@ -76,6 +72,7 @@ public final class ContainerProcess extends BaseProcess {
         private String logsDirectory;
     
         private String bundlesDirectory;
    +
         private String modelerDirectory;
     
         private boolean withDataSource;
    @@ -199,30 +196,15 @@ public void run(final AbstractUIProcessHandler handler, final String[] args) {
     
             MavenUtils mavenUtils = new MavenUtils(mavenDir, handler);
             File customMavenProxySettings = null;
    -        try {
    -            if (isProxyEnabled && mavenProxyAutoconf) {
    +        if (isProxyEnabled && mavenProxyAutoconf) {
    +            try {
                     customMavenProxySettings = MavenUtils.createSettingsWithProxy(
                             installPath, proxyHost, proxyPort, proxyUser, proxyPwd);
    +            } catch (Exception e) {
    +                StringBuilder message = new StringBuilder("Error during creation of custom Maven settings.xml");
    +                handler.emitError(message.toString(), e.getMessage());
    +                InstallLog.getInstance().error(message.append('\n').append(e.getMessage()).toString());
                 }
    -        } catch (IOException e) {
    -            StringBuilder message = new StringBuilder("I/O error during creation of Maven custom settings.xml");
    -            handler.emitError(message.toString(), e.getMessage());
    -            InstallLog.getInstance().error(message.append('\n').append(e.getMessage()).toString());
    -        } catch (ParserConfigurationException e) {
    -            StringBuilder message = new StringBuilder(
    -                    "Parser configuration error during creation of Maven custom settings.xml");
    -            handler.emitError(message.toString(), e.getMessage());
    -            InstallLog.getInstance().error(message.append('\n').append(e.getMessage()).toString());
    -        } catch (TransformerException e) {
    -            StringBuilder message = new StringBuilder(
    -                    "Transformer error during creation of Maven custom settings.xml");
    -            handler.emitError(message.toString(), e.getMessage());
    -            InstallLog.getInstance().error(message.append('\n').append(e.getMessage()).toString());
    -        } catch (SAXException e) {
    -            StringBuilder message = new StringBuilder(
    -                    "XML parsing error during creation of Maven custom settings.xml");
    -            handler.emitError(message.toString(), e.getMessage());
    -            InstallLog.getInstance().error(message.append('\n').append(e.getMessage()).toString());
             }
     
             Properties mvnProperties = new Properties();
    
  • installer/src/main/java/org/apache/syncope/installer/utilities/FileSystemUtils.java+2 1 modified
    @@ -30,6 +30,7 @@
     import java.io.OutputStreamWriter;
     import java.io.PrintWriter;
     import java.nio.charset.Charset;
    +import javax.xml.XMLConstants;
     import javax.xml.transform.OutputKeys;
     import javax.xml.transform.Transformer;
     import javax.xml.transform.TransformerException;
    @@ -135,7 +136,7 @@ public void appendToFile(final File file, final String content) {
     
         public static void writeXML(final Document doc, final OutputStream out) throws IOException, TransformerException {
             final TransformerFactory factory = TransformerFactory.newInstance();
    -        factory.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
    +        factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
             final Transformer transformer = factory.newTransformer();
             transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
             transformer.setOutputProperty(OutputKeys.METHOD, "xml");
    
  • installer/src/main/java/org/apache/syncope/installer/utilities/MavenUtils.java+15 15 modified
    @@ -20,8 +20,8 @@
     
     import com.izforge.izpack.panels.process.AbstractUIProcessHandler;
     import java.io.File;
    +import java.io.FileInputStream;
     import java.io.FileNotFoundException;
    -import java.io.IOException;
     import java.io.PrintStream;
     import java.nio.charset.StandardCharsets;
     import java.nio.file.Files;
    @@ -34,11 +34,6 @@
     import java.util.logging.Level;
     import java.util.logging.Logger;
     
    -import javax.xml.parsers.DocumentBuilder;
    -import javax.xml.parsers.DocumentBuilderFactory;
    -import javax.xml.parsers.ParserConfigurationException;
    -import javax.xml.transform.TransformerException;
    -
     import org.apache.commons.codec.binary.Hex;
     import org.apache.commons.io.FileUtils;
     import org.apache.maven.shared.invoker.DefaultInvocationRequest;
    @@ -51,7 +46,10 @@
     import org.apache.maven.shared.invoker.PrintStreamLogger;
     import org.w3c.dom.Document;
     import org.w3c.dom.Element;
    -import org.xml.sax.SAXException;
    +import org.w3c.dom.bootstrap.DOMImplementationRegistry;
    +import org.w3c.dom.ls.DOMImplementationLS;
    +import org.w3c.dom.ls.LSInput;
    +import org.w3c.dom.ls.LSParser;
     
     public class MavenUtils {
     
    @@ -85,7 +83,7 @@ public void archetypeGenerate(
             request.setBatchMode(true);
             final Properties properties =
                     archetypeProperties(archetypeVersion, groupId, artifactId, secretKey,
    -                                    anonymousKey, jwsKey, adminPassword);
    +                        anonymousKey, jwsKey, adminPassword);
             request.setProperties(properties);
             if (customSettingsFile != null && FileUtils.sizeOf(customSettingsFile) > 0) {
                 request.setUserSettingsFile(customSettingsFile);
    @@ -122,7 +120,7 @@ private Properties archetypeProperties(
                 try {
                     final MessageDigest cript = MessageDigest.getInstance("SHA-1");
                     String encodedPassword =
    -                    new String(Hex.encodeHex(cript.digest(adminPassword.getBytes(StandardCharsets.UTF_8))));
    +                        new String(Hex.encodeHex(cript.digest(adminPassword.getBytes(StandardCharsets.UTF_8))));
                     properties.setProperty("adminPassword", encodedPassword);
                 } catch (final NoSuchAlgorithmException ex) {
                     Logger.getLogger(MavenUtils.class.getName()).log(Level.SEVERE, "NoSuchAlgorithmException", ex);
    @@ -193,19 +191,21 @@ private InvocationResult invoke(final InvocationRequest request, final String pa
         }
     
         public static File createSettingsWithProxy(final String path, final String proxyHost, final String proxyPort,
    -            final String proxyUser, final String proxyPassword) throws IOException, ParserConfigurationException,
    -            TransformerException, SAXException {
    +            final String proxyUser, final String proxyPassword) throws Exception {
    +
             final File settingsXML = new File(System.getProperty(MAVEN_HOME_PROPERTY) + (System.getProperty(
                     MAVEN_HOME_PROPERTY).endsWith("/") ? "conf/settings.xml" : "/conf/settings.xml"));
             final File tempSettingsXML = new File(path + (path.endsWith("/") ? "settings_temp.xml" : "/settings_temp.xml"));
             if (settingsXML.canRead() && !tempSettingsXML.exists()) {
                 tempSettingsXML.createNewFile();
     
    -            final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    -            dbf.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
    -            final DocumentBuilder builder = dbf.newDocumentBuilder();
    +            DOMImplementationRegistry reg = DOMImplementationRegistry.newInstance();
    +            DOMImplementationLS domImpl = (DOMImplementationLS) reg.getDOMImplementation("LS");
    +            LSInput lsinput = domImpl.createLSInput();
    +            lsinput.setByteStream(new FileInputStream(settingsXML));
    +            LSParser parser = domImpl.createLSParser(DOMImplementationLS.MODE_SYNCHRONOUS, null);
                 // parse settings.xml
    -            final Document settings = builder.parse(settingsXML);
    +            final Document settings = parser.parse(lsinput);
     
                 final Element proxies = (Element) settings.getDocumentElement().getElementsByTagName("proxies").item(0);
     
    
ad31479c1c54

Ensuring secure XSLT processing everywhere

https://github.com/apache/syncopeFrancesco ChicchiriccòMar 7, 2018via ghsa
16 files changed · +209 186
  • client/cli/src/main/java/org/apache/syncope/client/cli/commands/configuration/ConfigurationExport.java+3 21 modified
    @@ -18,18 +18,12 @@
      */
     package org.apache.syncope.client.cli.commands.configuration;
     
    -import java.io.FileNotFoundException;
    -import java.io.IOException;
     import java.io.InputStream;
    -import javax.xml.parsers.ParserConfigurationException;
    -import javax.xml.transform.TransformerConfigurationException;
    -import javax.xml.transform.TransformerException;
     import org.apache.syncope.client.cli.Input;
     import org.apache.syncope.client.cli.util.XMLUtils;
     import org.apache.syncope.common.lib.SyncopeClientException;
     import org.slf4j.Logger;
     import org.slf4j.LoggerFactory;
    -import org.xml.sax.SAXException;
     
     public class ConfigurationExport extends AbstractConfigurationCommand {
     
    @@ -53,24 +47,12 @@ public void export() {
                             input.firstParameter() + EXPORT_FILE_NAME);
                     configurationResultManager.genericMessage(
                             input.firstParameter() + EXPORT_FILE_NAME + " successfully created");
    -            } catch (final IOException ex) {
    -                LOG.error("Error exporting configuration", ex);
    -                configurationResultManager.genericError(ex.getMessage());
    -            } catch (ParserConfigurationException | SAXException | TransformerConfigurationException ex) {
    -                LOG.error("Error exporting configuration", ex);
    -                configurationResultManager.genericError(
    -                        "Error creating " + input.firstParameter() + EXPORT_FILE_NAME + " " + ex.getMessage());
    -            } catch (final TransformerException ex) {
    -                LOG.error("Error exporting configuration", ex);
    -                if (ex.getCause() instanceof FileNotFoundException) {
    -                    configurationResultManager.genericError("Permission denied on " + input.firstParameter());
    -                } else {
    -                    configurationResultManager.genericError(
    -                            "Error creating " + input.firstParameter() + EXPORT_FILE_NAME + " " + ex.getMessage());
    -                }
                 } catch (final SyncopeClientException ex) {
                     LOG.error("Error exporting configuration", ex);
                     configurationResultManager.genericError("Error calling configuration service " + ex.getMessage());
    +            } catch (final Exception ex) {
    +                LOG.error("Error exporting configuration", ex);
    +                configurationResultManager.genericError(ex.getMessage());
                 }
             } else {
                 configurationResultManager.commandOptionError(EXPORT_HELP_MESSAGE);
    
  • client/cli/src/main/java/org/apache/syncope/client/cli/commands/report/ReportExportExecution.java+8 23 modified
    @@ -18,25 +18,19 @@
      */
     package org.apache.syncope.client.cli.commands.report;
     
    -import java.io.IOException;
     import java.util.Arrays;
    -import javax.xml.parsers.ParserConfigurationException;
    -import javax.xml.transform.TransformerException;
     import javax.xml.ws.WebServiceException;
     import org.apache.syncope.client.cli.Input;
    -import org.apache.syncope.client.cli.util.CommandUtils;
     import org.apache.syncope.common.lib.SyncopeClientException;
    -import org.apache.syncope.common.lib.types.ReportExecExportFormat;
     import org.slf4j.Logger;
     import org.slf4j.LoggerFactory;
    -import org.xml.sax.SAXException;
     
     public class ReportExportExecution extends AbstractReportCommand {
     
         private static final Logger LOG = LoggerFactory.getLogger(ReportExportExecution.class);
     
    -    private static final String EXPORT_EXECUTION_HELP_MESSAGE
    -            = "report --export-execution-result {EXECUTION-KEY} {EXECUTION-KEY} [...] {FORMAT}\n"
    +    private static final String EXPORT_EXECUTION_HELP_MESSAGE =
    +            "report --export-execution-result {EXECUTION-KEY} {EXECUTION-KEY} [...] {FORMAT}\n"
                 + "          Format: CSV / HTML / PDF / XML / RTF";
     
         private final Input input;
    @@ -50,28 +44,19 @@ public void export() {
                 final String[] parameters = Arrays.copyOf(input.getParameters(), input.parameterNumber() - 1);
                 for (final String parameter : parameters) {
                     try {
    -                    final String result = reportSyncopeOperations.exportExecutionResult(
    -                            parameter, input.lastParameter());
    +                    String result = reportSyncopeOperations.exportExecutionResult(parameter, input.lastParameter());
                         reportResultManager.genericMessage(result + "created.");
    -                } catch (final WebServiceException | SyncopeClientException ex) {
    -                    LOG.error("Error exporting execution", ex);
    -                    if (ex.getMessage().startsWith("NotFound")) {
    +                } catch (final WebServiceException | SyncopeClientException e) {
    +                    LOG.error("Error exporting execution", e);
    +                    if (e.getMessage().startsWith("NotFound")) {
                             reportResultManager.notFoundError("Report", parameter);
                         } else {
    -                        reportResultManager.genericError(ex.getMessage());
    +                        reportResultManager.genericError(e.getMessage());
                         }
    -                } catch (final NumberFormatException ex) {
    -                    LOG.error("Error exporting execution", ex);
    -                    reportResultManager.numberFormatException("report", parameter);
    -                } catch (IOException | ParserConfigurationException | SAXException | TransformerException e) {
    +                } catch (final Exception e) {
                         LOG.error("Error exporting execution", e);
                         reportResultManager.genericError(
                                 " - Error creating " + "export_" + parameter + " " + e.getMessage());
    -                } catch (final IllegalArgumentException ex) {
    -                    LOG.error("Error exporting execution", ex);
    -                    reportResultManager.typeNotValidError(
    -                            "format", input.firstParameter(),
    -                            CommandUtils.fromEnumToArray(ReportExecExportFormat.class));
                     }
                     break;
                 }
    
  • client/cli/src/main/java/org/apache/syncope/client/cli/commands/report/ReportSyncopeOperations.java+1 5 modified
    @@ -18,14 +18,11 @@
      */
     package org.apache.syncope.client.cli.commands.report;
     
    -import java.io.IOException;
     import java.io.OutputStream;
     import java.io.SequenceInputStream;
     import java.nio.file.Files;
     import java.nio.file.Paths;
     import java.util.List;
    -import javax.xml.parsers.ParserConfigurationException;
    -import javax.xml.transform.TransformerException;
     import org.apache.cxf.helpers.IOUtils;
     import org.apache.syncope.client.cli.SyncopeServices;
     import org.apache.syncope.client.cli.util.XMLUtils;
    @@ -34,7 +31,6 @@
     import org.apache.syncope.common.lib.types.ReportExecExportFormat;
     import org.apache.syncope.common.rest.api.beans.ExecuteQuery;
     import org.apache.syncope.common.rest.api.service.ReportService;
    -import org.xml.sax.SAXException;
     
     public class ReportSyncopeOperations {
     
    @@ -53,7 +49,7 @@ public List<ReportTO> list() {
         }
     
         public String exportExecutionResult(final String executionKey, final String reportExecExportFormat)
    -            throws TransformerException, SAXException, IOException, ParserConfigurationException {
    +            throws Exception {
     
             ReportExecExportFormat format = ReportExecExportFormat.valueOf(reportExecExportFormat);
             SequenceInputStream report = (SequenceInputStream) reportService.exportExecutionResult(executionKey, format).
    
  • client/cli/src/main/java/org/apache/syncope/client/cli/util/XMLUtils.java+14 19 modified
    @@ -19,34 +19,29 @@
     package org.apache.syncope.client.cli.util;
     
     import java.io.File;
    -import java.io.IOException;
     import java.io.InputStream;
    -import java.io.StringReader;
    -import javax.xml.parsers.DocumentBuilderFactory;
    -import javax.xml.parsers.ParserConfigurationException;
    -import javax.xml.transform.TransformerException;
    +import javax.xml.XMLConstants;
     import javax.xml.transform.TransformerFactory;
     import javax.xml.transform.dom.DOMSource;
     import javax.xml.transform.stream.StreamResult;
    -import org.apache.cxf.helpers.IOUtils;
    -import org.xml.sax.InputSource;
    -import org.xml.sax.SAXException;
    +import org.w3c.dom.bootstrap.DOMImplementationRegistry;
    +import org.w3c.dom.ls.DOMImplementationLS;
    +import org.w3c.dom.ls.LSInput;
    +import org.w3c.dom.ls.LSParser;
     
     public final class XMLUtils {
     
    -    public static void createXMLFile(final InputStream sis, final String filePath)
    -            throws TransformerException, SAXException, IOException, ParserConfigurationException {
    -
    -        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    -        dbf.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
    +    public static void createXMLFile(final InputStream sis, final String filePath) throws Exception {
    +        DOMImplementationRegistry reg = DOMImplementationRegistry.newInstance();
    +        DOMImplementationLS domImpl = (DOMImplementationLS) reg.getDOMImplementation("LS");
    +        LSInput lsinput = domImpl.createLSInput();
    +        lsinput.setByteStream(sis);
    +        LSParser parser = domImpl.createLSParser(DOMImplementationLS.MODE_SYNCHRONOUS, null);
     
             TransformerFactory tf = TransformerFactory.newInstance();
    -        tf.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
    -        
    -        tf.newTransformer().
    -                transform(new DOMSource(dbf.newDocumentBuilder().
    -                        parse(new InputSource(new StringReader(IOUtils.toString(sis))))),
    -                        new StreamResult(new File(filePath)));
    +        tf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
    +
    +        tf.newTransformer().transform(new DOMSource(parser.parse(lsinput)), new StreamResult(new File(filePath)));
         }
     
         private XMLUtils() {
    
  • core/logic/src/main/java/org/apache/syncope/core/logic/cocoon/XSLTTransformer.java+11 3 modified
    @@ -19,11 +19,11 @@
     import java.util.HashMap;
     import java.util.Map;
     import java.util.regex.Pattern;
    +import javax.xml.XMLConstants;
     import javax.xml.transform.Source;
     import javax.xml.transform.Templates;
     import javax.xml.transform.Transformer;
     import javax.xml.transform.TransformerConfigurationException;
    -import javax.xml.transform.TransformerFactory;
     import javax.xml.transform.sax.SAXResult;
     import javax.xml.transform.sax.SAXTransformerFactory;
     import javax.xml.transform.sax.TransformerHandler;
    @@ -34,6 +34,7 @@
     import org.apache.cocoon.sax.AbstractSAXTransformer;
     import org.apache.cocoon.sax.SAXConsumer;
     import org.apache.cocoon.sax.util.SAXConsumerAdapter;
    +import org.apache.syncope.core.provisioning.java.utils.VoidURIResolver;
     import org.slf4j.Logger;
     import org.slf4j.LoggerFactory;
     
    @@ -132,7 +133,7 @@ public void setParameters(final Map<String, ? extends Object> parameters) {
             if (parameters == null) {
                 this.parameters = null;
             } else {
    -            this.parameters = new HashMap<String, Object>(parameters);
    +            this.parameters = new HashMap<>(parameters);
             }
         }
     
    @@ -183,7 +184,14 @@ public CacheKey constructCacheKey() {
          * @return a new transformer factory
          */
         private static SAXTransformerFactory createNewSAXTransformerFactory() {
    -        return (SAXTransformerFactory) TransformerFactory.newInstance();
    +        SAXTransformerFactory transformerFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
    +        transformerFactory.setURIResolver(new VoidURIResolver());
    +        try {
    +            transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
    +        } catch (TransformerConfigurationException e) {
    +            LOG.error("Could not enable secure XML processing", e);
    +        }
    +        return transformerFactory;
         }
     
         @Override
    
  • core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentExporter.java+3 2 modified
    @@ -42,6 +42,7 @@
     import java.util.TreeMap;
     import java.util.TreeSet;
     import javax.sql.DataSource;
    +import javax.xml.XMLConstants;
     import javax.xml.transform.OutputKeys;
     import javax.xml.transform.Transformer;
     import javax.xml.transform.TransformerConfigurationException;
    @@ -367,8 +368,8 @@ public void export(
             }
     
             StreamResult streamResult = new StreamResult(os);
    -        final SAXTransformerFactory transformerFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
    -        transformerFactory.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
    +        SAXTransformerFactory transformerFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
    +        transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
     
             TransformerHandler handler = transformerFactory.newTransformerHandler();
             Transformer serializer = handler.getTransformer();
    
  • core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/DefaultReportJobDelegate.java+17 3 modified
    @@ -27,8 +27,11 @@
     import java.util.zip.Deflater;
     import java.util.zip.ZipEntry;
     import java.util.zip.ZipOutputStream;
    +import javax.xml.XMLConstants;
     import javax.xml.transform.OutputKeys;
     import javax.xml.transform.Transformer;
    +import javax.xml.transform.TransformerConfigurationException;
    +import javax.xml.transform.TransformerFactory;
     import javax.xml.transform.sax.SAXTransformerFactory;
     import javax.xml.transform.sax.TransformerHandler;
     import javax.xml.transform.stream.StreamResult;
    @@ -44,6 +47,7 @@
     import org.apache.syncope.core.persistence.api.entity.Report;
     import org.apache.syncope.core.persistence.api.entity.ReportExec;
     import org.apache.syncope.core.provisioning.api.job.report.ReportJobDelegate;
    +import org.apache.syncope.core.provisioning.java.utils.VoidURIResolver;
     import org.quartz.JobExecutionException;
     import org.slf4j.Logger;
     import org.slf4j.LoggerFactory;
    @@ -58,6 +62,18 @@ public class DefaultReportJobDelegate implements ReportJobDelegate {
     
         private static final Logger LOG = LoggerFactory.getLogger(ReportJobDelegate.class);
     
    +    private static final SAXTransformerFactory TRANSFORMER_FACTORY;
    +
    +    static {
    +        TRANSFORMER_FACTORY = (SAXTransformerFactory) TransformerFactory.newInstance();
    +        TRANSFORMER_FACTORY.setURIResolver(new VoidURIResolver());
    +        try {
    +            TRANSFORMER_FACTORY.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
    +        } catch (TransformerConfigurationException e) {
    +            LOG.error("Could not enable secure XML processing", e);
    +        }
    +    }
    +
         /**
          * Report DAO.
          */
    @@ -127,9 +143,7 @@ public void execute(final String reportKey) throws JobExecutionException {
             ZipOutputStream zos = new ZipOutputStream(baos);
             zos.setLevel(Deflater.BEST_COMPRESSION);
             try {
    -            SAXTransformerFactory tFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
    -            tFactory.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
    -            handler = tFactory.newTransformerHandler();
    +            handler = TRANSFORMER_FACTORY.newTransformerHandler();
                 Transformer serializer = handler.getTransformer();
                 serializer.setOutputProperty(OutputKeys.ENCODING, StandardCharsets.UTF_8.name());
                 serializer.setOutputProperty(OutputKeys.INDENT, "yes");
    
  • core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/VoidURIResolver.java+35 0 added
    @@ -0,0 +1,35 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one
    + * or more contributor license agreements.  See the NOTICE file
    + * distributed with this work for additional information
    + * regarding copyright ownership.  The ASF 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.apache.syncope.core.provisioning.java.utils;
    +
    +import javax.xml.transform.Source;
    +import javax.xml.transform.TransformerException;
    +import javax.xml.transform.URIResolver;
    +
    +/**
    + * This implementation disallows any XSLT include, for security reasons.
    + */
    +public class VoidURIResolver implements URIResolver {
    +
    +    @Override
    +    public Source resolve(final String href, final String base) throws TransformerException {
    +        return null;
    +    }
    +
    +}
    
  • ext/camel/logic/src/main/java/org/apache/syncope/core/logic/init/CamelRouteLoader.java+7 7 modified
    @@ -21,22 +21,23 @@
     import java.io.StringWriter;
     import java.util.Map;
     import javax.sql.DataSource;
    -import javax.xml.parsers.DocumentBuilder;
    -import javax.xml.parsers.DocumentBuilderFactory;
    +import javax.xml.XMLConstants;
     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 org.apache.commons.lang3.StringUtils;
    +import org.apache.cxf.staxutils.StaxUtils;
     import org.apache.syncope.common.lib.types.AnyTypeKind;
     import org.apache.syncope.common.lib.types.CamelEntitlement;
     import org.apache.syncope.core.provisioning.api.EntitlementsHolder;
     import org.apache.syncope.core.spring.ResourceWithFallbackLoader;
     import org.apache.syncope.core.persistence.api.DomainsHolder;
     import org.apache.syncope.core.persistence.api.SyncopeLoader;
     import org.apache.syncope.core.persistence.api.entity.CamelRoute;
    +import org.apache.syncope.core.provisioning.java.utils.VoidURIResolver;
     import org.slf4j.Logger;
     import org.slf4j.LoggerFactory;
     import org.springframework.beans.factory.annotation.Autowired;
    @@ -153,11 +154,10 @@ private void loadRoutes(
                     NodeList routeNodes;
                     if (IS_JBOSS) {
                         tf = TransformerFactory.newInstance();
    -                    tf.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
    -                    DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
    -                    dbFactory.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
    -                    DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
    -                    Document doc = dBuilder.parse(resource.getInputStream());
    +                    tf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
    +                    tf.setURIResolver(new VoidURIResolver());
    +
    +                    Document doc = StaxUtils.read(resource.getInputStream());
     
                         routeNodes = doc.getDocumentElement().getElementsByTagName("route");
                     } else {
    
  • ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2ReaderWriter.java+4 2 modified
    @@ -50,6 +50,7 @@
     import org.apache.syncope.common.lib.types.SAML2BindingType;
     import org.apache.syncope.common.lib.types.SignatureAlgorithm;
     import org.apache.syncope.core.logic.init.SAML2SPLoader;
    +import org.apache.syncope.core.provisioning.java.utils.VoidURIResolver;
     import org.apache.wss4j.common.crypto.Merlin;
     import org.apache.wss4j.common.ext.WSSecurityException;
     import org.apache.wss4j.common.saml.OpenSAMLUtil;
    @@ -77,6 +78,7 @@ public class SAML2ReaderWriter {
     
         static {
             TRANSFORMER_FACTORY = TransformerFactory.newInstance();
    +        TRANSFORMER_FACTORY.setURIResolver(new VoidURIResolver());
             try {
                 TRANSFORMER_FACTORY.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
             } catch (TransformerConfigurationException e) {
    @@ -103,14 +105,14 @@ public void init() {
             // Try to load a signature algorithm
             if (loader.getSignatureAlgorithm() != null) {
                 SignatureAlgorithm loadedSignatureAlgorithm =
    -                SignatureAlgorithm.valueOf(loader.getSignatureAlgorithm());
    +                    SignatureAlgorithm.valueOf(loader.getSignatureAlgorithm());
                 if (loadedSignatureAlgorithm != null) {
                     sigAlgo = loadedSignatureAlgorithm.getAlgorithm();
                     jceSigAlgo = JCEMapper.translateURItoJCEID(sigAlgo);
                 }
                 if (jceSigAlgo == null) {
                     LOG.warn("Signature algorithm {} is not valid. Using default algorithm instead.",
    -                         loader.getSignatureAlgorithm());
    +                        loader.getSignatureAlgorithm());
                     sigAlgo = null;
                 }
             }
    
  • fit/core-reference/src/test/java/org/apache/syncope/fit/core/ReportITCase.java+38 33 modified
    @@ -54,6 +54,40 @@
     
     public class ReportITCase extends AbstractITCase {
     
    +    protected static String execReport(final String reportKey) {
    +        ReportTO reportTO = reportService.read(reportKey);
    +        assertNotNull(reportTO);
    +        assertNotNull(reportTO.getExecutions());
    +
    +        int preExecSize = reportTO.getExecutions().size();
    +        ExecTO exec = reportService.execute(new ExecuteQuery.Builder().key(reportKey).build());
    +        assertNotNull(exec);
    +
    +        int i = 0;
    +        int maxit = 50;
    +
    +        // wait for completion (executions incremented)
    +        do {
    +            try {
    +                Thread.sleep(1000);
    +            } catch (InterruptedException e) {
    +            }
    +
    +            reportTO = reportService.read(reportKey);
    +
    +            assertNotNull(reportTO);
    +            assertNotNull(reportTO.getExecutions());
    +
    +            i++;
    +        } while (preExecSize == reportTO.getExecutions().size() && i < maxit);
    +        if (i == maxit) {
    +            fail("Timeout when executing report " + reportKey);
    +        }
    +        exec = reportTO.getExecutions().get(reportTO.getExecutions().size() - 1);
    +        assertEquals(ReportExecStatus.SUCCESS.name(), exec.getStatus());
    +        return exec.getKey();
    +    }
    +
         @Test
         public void getReportletConfs() {
             Set<String> reportletConfs = syncopeService.platform().getReportletConfs();
    @@ -139,35 +173,6 @@ public void delete() {
             }
         }
     
    -    private String execute(final String reportKey) {
    -        ExecTO execution = reportService.execute(new ExecuteQuery.Builder().key(reportKey).build());
    -        assertNotNull(execution);
    -
    -        int i = 0;
    -        int maxit = 50;
    -
    -        ReportTO reportTO;
    -
    -        // wait for report execution completion (executions incremented)
    -        do {
    -            try {
    -                Thread.sleep(1000);
    -            } catch (InterruptedException e) {
    -            }
    -
    -            reportTO = reportService.read(reportKey);
    -
    -            assertNotNull(reportTO);
    -            assertNotNull(reportTO.getExecutions());
    -
    -            i++;
    -        } while (reportTO.getExecutions().isEmpty()
    -                || (!ReportExecStatus.SUCCESS.name().equals(reportTO.getExecutions().get(0).getStatus()) && i < maxit));
    -        assertEquals(ReportExecStatus.SUCCESS.name(), reportTO.getExecutions().get(0).getStatus());
    -
    -        return reportTO.getExecutions().get(0).getKey();
    -    }
    -
         private void checkExport(final String execKey, final ReportExecExportFormat fmt) throws IOException {
             Response response = reportService.exportExecutionResult(execKey, fmt);
             assertNotNull(response);
    @@ -192,7 +197,7 @@ public void executeAndExport() throws IOException {
             assertNotNull(reportTO);
     
             try {
    -            execute(reportTO.getKey());
    +            execReport(reportTO.getKey());
                 fail();
             } catch (SyncopeClientException e) {
                 assertEquals(ClientExceptionType.Scheduling, e.getType());
    @@ -202,7 +207,7 @@ public void executeAndExport() throws IOException {
             reportTO.setActive(true);
             reportService.update(reportTO);
     
    -        String execKey = execute(reportTO.getKey());
    +        String execKey = execReport(reportTO.getKey());
     
             checkExport(execKey, ReportExecExportFormat.XML);
             checkExport(execKey, ReportExecExportFormat.HTML);
    @@ -226,7 +231,7 @@ public void deleteExecutions() {
             reportTO = createReport(reportTO);
             assertNotNull(reportTO);
     
    -        String execKey = execute(reportTO.getKey());
    +        String execKey = execReport(reportTO.getKey());
             assertNotNull(execKey);
     
             try {
    @@ -266,7 +271,7 @@ public void auditReport() throws IOException {
                 report.setTemplate("sample");
                 report = createReport(report);
     
    -            String execKey = execute(report.getKey());
    +            String execKey = execReport(report.getKey());
                 checkExport(execKey, ReportExecExportFormat.XML);
     
                 report = reportService.read(report.getKey());
    
  • fit/core-reference/src/test/java/org/apache/syncope/fit/core/ReportTemplateITCase.java+36 0 modified
    @@ -34,6 +34,7 @@
     import org.apache.syncope.common.lib.SyncopeClientException;
     import org.apache.syncope.common.lib.to.ReportTemplateTO;
     import org.apache.syncope.common.lib.types.ClientExceptionType;
    +import org.apache.syncope.common.lib.types.ReportExecExportFormat;
     import org.apache.syncope.common.lib.types.ReportTemplateFormat;
     import org.apache.syncope.fit.AbstractITCase;
     import org.junit.Test;
    @@ -150,6 +151,41 @@ public void crud() throws IOException {
             }
         }
     
    +    @Test
    +    public void safeTemplate() throws IOException {
    +        Response response = reportTemplateService.getFormat("sample", ReportTemplateFormat.HTML);
    +        String before = IOUtils.toString((InputStream) response.getEntity(), StandardCharsets.UTF_8);
    +        assertNotNull(before);
    +
    +        String execKey = ReportITCase.execReport("0062ea9c-924d-4ecf-9961-4492a8cc6d1b");
    +        assertNotNull(execKey);
    +        response = reportService.exportExecutionResult(execKey, ReportExecExportFormat.HTML);
    +        String result = IOUtils.toString((InputStream) response.getEntity(), StandardCharsets.UTF_8);
    +        assertNotNull(result);
    +        assertTrue(result.startsWith("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
    +                + "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" "
    +                + "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"));
    +
    +        String malicious = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
    +                + "<!DOCTYPE xsl:stylesheet "
    +                + "[<!ENTITY file SYSTEM \"webapps/syncope/WEB-INF/classes/security.properties\">]>\n"
    +                + "<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n"
    +                + "    <xsl:template match=\"/\">&file;</xsl:template>\n"
    +                + "</xsl:stylesheet>";
    +        try {
    +            reportTemplateService.setFormat("sample", ReportTemplateFormat.HTML,
    +                    IOUtils.toInputStream(malicious, StandardCharsets.UTF_8));
    +
    +            response = reportService.exportExecutionResult(execKey, ReportExecExportFormat.HTML);
    +            result = IOUtils.toString((InputStream) response.getEntity(), StandardCharsets.UTF_8);
    +            assertNotNull(result);
    +            assertTrue(result.isEmpty());
    +        } finally {
    +            reportTemplateService.setFormat("sample", ReportTemplateFormat.HTML,
    +                    IOUtils.toInputStream(before, StandardCharsets.UTF_8));
    +        }
    +    }
    +
         @Test
         public void issueSYNCOPE866() {
             ReportTemplateTO reportTemplateTO = new ReportTemplateTO();
    
  • installer/src/main/java/org/apache/syncope/installer/processes/ArchetypeProcess.java+8 27 modified
    @@ -21,16 +21,12 @@
     import org.apache.syncope.installer.utilities.FileSystemUtils;
     import com.izforge.izpack.panels.process.AbstractUIProcessHandler;
     import java.io.File;
    -import java.io.IOException;
     import java.util.Properties;
    -import javax.xml.parsers.ParserConfigurationException;
    -import javax.xml.transform.TransformerException;
     import org.apache.syncope.installer.files.ConsolePom;
     import org.apache.syncope.installer.files.CorePom;
     import org.apache.syncope.installer.files.ParentPom;
     import org.apache.syncope.installer.utilities.InstallLog;
     import org.apache.syncope.installer.utilities.MavenUtils;
    -import org.xml.sax.SAXException;
     
     public class ArchetypeProcess extends BaseProcess {
     
    @@ -65,30 +61,15 @@ public void run(final AbstractUIProcessHandler handler, final String[] args) {
             InstallLog.initialize(installPath, handler);
             MavenUtils mavenUtils = new MavenUtils(mavenDir, handler);
             File customMavenProxySettings = null;
    -        try {
    -            if (isProxyEnabled && mavenProxyAutoconf) {
    -                customMavenProxySettings = MavenUtils.createSettingsWithProxy(installPath, proxyHost, proxyPort,
    -                        proxyUser, proxyPwd);
    +        if (isProxyEnabled && mavenProxyAutoconf) {
    +            try {
    +                customMavenProxySettings =
    +                        MavenUtils.createSettingsWithProxy(installPath, proxyHost, proxyPort, proxyUser, proxyPwd);
    +            } catch (Exception e) {
    +                StringBuilder message = new StringBuilder("Error during creation of custom Maven settings.xml");
    +                handler.emitError(message.toString(), e.getMessage());
    +                InstallLog.getInstance().error(message.append('\n').append(e.getMessage()).toString());
                 }
    -        } catch (IOException e) {
    -            StringBuilder message = new StringBuilder("I/O error during creation of Maven custom settings.xml");
    -            handler.emitError(message.toString(), e.getMessage());
    -            InstallLog.getInstance().error(message.append('\n').append(e.getMessage()).toString());
    -        } catch (ParserConfigurationException e) {
    -            StringBuilder message = new StringBuilder(
    -                    "Parser configuration error during creation of Maven custom settings.xml");
    -            handler.emitError(message.toString(), e.getMessage());
    -            InstallLog.getInstance().error(message.append('\n').append(e.getMessage()).toString());
    -        } catch (TransformerException e) {
    -            StringBuilder message = new StringBuilder(
    -                    "Transformer error during creation of Maven custom settings.xml");
    -            handler.emitError(message.toString(), e.getMessage());
    -            InstallLog.getInstance().error(message.append('\n').append(e.getMessage()).toString());
    -        } catch (SAXException e) {
    -            StringBuilder message = new StringBuilder(
    -                    "XML parsing error during creation of Maven custom settings.xml");
    -            handler.emitError(message.toString(), e.getMessage());
    -            InstallLog.getInstance().error(message.append('\n').append(e.getMessage()).toString());
             }
     
             handler.logOutput("########################## IMPORTANT ##########################", true);
    
  • installer/src/main/java/org/apache/syncope/installer/processes/ContainerProcess.java+7 25 modified
    @@ -21,10 +21,7 @@
     import org.apache.syncope.installer.utilities.FileSystemUtils;
     import com.izforge.izpack.panels.process.AbstractUIProcessHandler;
     import java.io.File;
    -import java.io.IOException;
     import java.util.Properties;
    -import javax.xml.parsers.ParserConfigurationException;
    -import javax.xml.transform.TransformerException;
     import org.apache.syncope.installer.containers.Glassfish;
     import org.apache.syncope.installer.containers.Tomcat;
     import org.apache.syncope.installer.containers.jboss.JBoss;
    @@ -35,7 +32,6 @@
     import org.apache.syncope.installer.files.MasterDomainXml;
     import org.apache.syncope.installer.utilities.InstallLog;
     import org.apache.syncope.installer.utilities.MavenUtils;
    -import org.xml.sax.SAXException;
     
     public final class ContainerProcess extends BaseProcess {
     
    @@ -76,6 +72,7 @@ public final class ContainerProcess extends BaseProcess {
         private String logsDirectory;
     
         private String bundlesDirectory;
    +
         private String modelerDirectory;
     
         private boolean withDataSource;
    @@ -199,30 +196,15 @@ public void run(final AbstractUIProcessHandler handler, final String[] args) {
     
             MavenUtils mavenUtils = new MavenUtils(mavenDir, handler);
             File customMavenProxySettings = null;
    -        try {
    -            if (isProxyEnabled && mavenProxyAutoconf) {
    +        if (isProxyEnabled && mavenProxyAutoconf) {
    +            try {
                     customMavenProxySettings = MavenUtils.createSettingsWithProxy(
                             installPath, proxyHost, proxyPort, proxyUser, proxyPwd);
    +            } catch (Exception e) {
    +                StringBuilder message = new StringBuilder("Error during creation of custom Maven settings.xml");
    +                handler.emitError(message.toString(), e.getMessage());
    +                InstallLog.getInstance().error(message.append('\n').append(e.getMessage()).toString());
                 }
    -        } catch (IOException e) {
    -            StringBuilder message = new StringBuilder("I/O error during creation of Maven custom settings.xml");
    -            handler.emitError(message.toString(), e.getMessage());
    -            InstallLog.getInstance().error(message.append('\n').append(e.getMessage()).toString());
    -        } catch (ParserConfigurationException e) {
    -            StringBuilder message = new StringBuilder(
    -                    "Parser configuration error during creation of Maven custom settings.xml");
    -            handler.emitError(message.toString(), e.getMessage());
    -            InstallLog.getInstance().error(message.append('\n').append(e.getMessage()).toString());
    -        } catch (TransformerException e) {
    -            StringBuilder message = new StringBuilder(
    -                    "Transformer error during creation of Maven custom settings.xml");
    -            handler.emitError(message.toString(), e.getMessage());
    -            InstallLog.getInstance().error(message.append('\n').append(e.getMessage()).toString());
    -        } catch (SAXException e) {
    -            StringBuilder message = new StringBuilder(
    -                    "XML parsing error during creation of Maven custom settings.xml");
    -            handler.emitError(message.toString(), e.getMessage());
    -            InstallLog.getInstance().error(message.append('\n').append(e.getMessage()).toString());
             }
     
             Properties mvnProperties = new Properties();
    
  • installer/src/main/java/org/apache/syncope/installer/utilities/FileSystemUtils.java+2 1 modified
    @@ -30,6 +30,7 @@
     import java.io.OutputStreamWriter;
     import java.io.PrintWriter;
     import java.nio.charset.Charset;
    +import javax.xml.XMLConstants;
     import javax.xml.transform.OutputKeys;
     import javax.xml.transform.Transformer;
     import javax.xml.transform.TransformerException;
    @@ -135,7 +136,7 @@ public void appendToFile(final File file, final String content) {
     
         public static void writeXML(final Document doc, final OutputStream out) throws IOException, TransformerException {
             final TransformerFactory factory = TransformerFactory.newInstance();
    -        factory.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
    +        factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
             final Transformer transformer = factory.newTransformer();
             transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
             transformer.setOutputProperty(OutputKeys.METHOD, "xml");
    
  • installer/src/main/java/org/apache/syncope/installer/utilities/MavenUtils.java+15 15 modified
    @@ -20,8 +20,8 @@
     
     import com.izforge.izpack.panels.process.AbstractUIProcessHandler;
     import java.io.File;
    +import java.io.FileInputStream;
     import java.io.FileNotFoundException;
    -import java.io.IOException;
     import java.io.PrintStream;
     import java.nio.charset.StandardCharsets;
     import java.nio.file.Files;
    @@ -34,11 +34,6 @@
     import java.util.logging.Level;
     import java.util.logging.Logger;
     
    -import javax.xml.parsers.DocumentBuilder;
    -import javax.xml.parsers.DocumentBuilderFactory;
    -import javax.xml.parsers.ParserConfigurationException;
    -import javax.xml.transform.TransformerException;
    -
     import org.apache.commons.codec.binary.Hex;
     import org.apache.commons.io.FileUtils;
     import org.apache.maven.shared.invoker.DefaultInvocationRequest;
    @@ -51,7 +46,10 @@
     import org.apache.maven.shared.invoker.PrintStreamLogger;
     import org.w3c.dom.Document;
     import org.w3c.dom.Element;
    -import org.xml.sax.SAXException;
    +import org.w3c.dom.bootstrap.DOMImplementationRegistry;
    +import org.w3c.dom.ls.DOMImplementationLS;
    +import org.w3c.dom.ls.LSInput;
    +import org.w3c.dom.ls.LSParser;
     
     public class MavenUtils {
     
    @@ -85,7 +83,7 @@ public void archetypeGenerate(
             request.setBatchMode(true);
             final Properties properties =
                     archetypeProperties(archetypeVersion, groupId, artifactId, secretKey,
    -                                    anonymousKey, jwsKey, adminPassword);
    +                        anonymousKey, jwsKey, adminPassword);
             request.setProperties(properties);
             if (customSettingsFile != null && FileUtils.sizeOf(customSettingsFile) > 0) {
                 request.setUserSettingsFile(customSettingsFile);
    @@ -122,7 +120,7 @@ private Properties archetypeProperties(
                 try {
                     final MessageDigest cript = MessageDigest.getInstance("SHA-1");
                     String encodedPassword =
    -                    new String(Hex.encodeHex(cript.digest(adminPassword.getBytes(StandardCharsets.UTF_8))));
    +                        new String(Hex.encodeHex(cript.digest(adminPassword.getBytes(StandardCharsets.UTF_8))));
                     properties.setProperty("adminPassword", encodedPassword);
                 } catch (final NoSuchAlgorithmException ex) {
                     Logger.getLogger(MavenUtils.class.getName()).log(Level.SEVERE, "NoSuchAlgorithmException", ex);
    @@ -193,19 +191,21 @@ private InvocationResult invoke(final InvocationRequest request, final String pa
         }
     
         public static File createSettingsWithProxy(final String path, final String proxyHost, final String proxyPort,
    -            final String proxyUser, final String proxyPassword) throws IOException, ParserConfigurationException,
    -            TransformerException, SAXException {
    +            final String proxyUser, final String proxyPassword) throws Exception {
    +
             final File settingsXML = new File(System.getProperty(MAVEN_HOME_PROPERTY) + (System.getProperty(
                     MAVEN_HOME_PROPERTY).endsWith("/") ? "conf/settings.xml" : "/conf/settings.xml"));
             final File tempSettingsXML = new File(path + (path.endsWith("/") ? "settings_temp.xml" : "/settings_temp.xml"));
             if (settingsXML.canRead() && !tempSettingsXML.exists()) {
                 tempSettingsXML.createNewFile();
     
    -            final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    -            dbf.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
    -            final DocumentBuilder builder = dbf.newDocumentBuilder();
    +            DOMImplementationRegistry reg = DOMImplementationRegistry.newInstance();
    +            DOMImplementationLS domImpl = (DOMImplementationLS) reg.getDOMImplementation("LS");
    +            LSInput lsinput = domImpl.createLSInput();
    +            lsinput.setByteStream(new FileInputStream(settingsXML));
    +            LSParser parser = domImpl.createLSParser(DOMImplementationLS.MODE_SYNCHRONOUS, null);
                 // parse settings.xml
    -            final Document settings = builder.parse(settingsXML);
    +            final Document settings = parser.parse(lsinput);
     
                 final Element proxies = (Element) settings.getDocumentElement().getElementsByTagName("proxies").item(0);
     
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

8

News mentions

0

No linked articles in our index yet.