support multiple configuration

This commit is contained in:
Andreas Penski 2021-05-21 11:16:20 +00:00
parent 730d7fefe9
commit 2e6efdd16f
59 changed files with 1136 additions and 608 deletions

View file

@ -34,7 +34,6 @@ import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.apache.commons.lang3.StringUtils;
import org.w3c.dom.ls.LSResourceResolver;
import org.xml.sax.SAXException;
import lombok.Getter;
@ -67,8 +66,6 @@ import net.sf.saxon.s9api.XsltExecutable;
@Slf4j
public class ContentRepository {
private Schema reportInputSchema;
@Getter
private final Processor processor;
@ -90,10 +87,10 @@ public class ContentRepository {
* @param strategy the security and resolving strategy
* @param repository the repository.
*/
public ContentRepository(final ResolvingConfigurationStrategy strategy, final URI repository) {
public ContentRepository(final Processor processor, final ResolvingConfigurationStrategy strategy, final URI repository) {
this.repository = repository;
this.resolvingConfigurationStrategy = strategy;
this.processor = this.resolvingConfigurationStrategy.getProcessor();
this.processor = processor;
this.resolver = this.resolvingConfigurationStrategy.createResolver(repository);
this.unparsedTextURIResolver = this.resolvingConfigurationStrategy.createUnparsedTextURIResolver(repository);
this.schemaFactory = this.resolvingConfigurationStrategy.createSchemaFactory();
@ -108,20 +105,15 @@ public class ContentRepository {
}
}
private Schema createSchema(final Source[] schemaSources, final LSResourceResolver resourceResolver) {
private Schema createSchema(final Source[] schemaSources) {
try {
final SchemaFactory sf = this.schemaFactory;
sf.setResourceResolver(resourceResolver);
return sf.newSchema(schemaSources);
this.schemaFactory.setResourceResolver(null);
return this.schemaFactory.newSchema(schemaSources);
} catch (final SAXException e) {
throw new IllegalArgumentException("Can not load schema from sources " + schemaSources[0].getSystemId(), e);
}
}
private Schema createSchema(final Source[] schemaSources) {
return createSchema(schemaSources, null);
}
/**
* Lädt ein XSL von der angegebenen URI
*
@ -158,40 +150,13 @@ public class ContentRepository {
* @return das erzeugte Schema
*/
public Schema createSchema(final URL url) {
return createSchema(url, null);
return createSchema(new Source[] { resolve(url) });
}
public Schema createSchema(final URI uri) {
return createSchema(new Source[] { resolveInRepository(uri) });
}
public Schema createSchema(final URL url, final LSResourceResolver resourceResolver) {
log.info("Load schema from source {}", url.getPath());
return createSchema(new Source[] { resolve(url) }, resourceResolver);
}
/**
* Liefert das definiert Schema für die Szenario-Konfiguration
*
* @return Scenario-Schema
*/
public Schema getScenarioSchema() {
return createSchema(ContentRepository.class.getResource("/xsd/scenarios.xsd"));
}
/**
* Liefert das definierte Schema für die Validierung des [@link CreateReportInput}
*
* @return ReportInput-Schema
*/
public Schema getReportInputSchema() {
if (this.reportInputSchema == null) {
final Source source = resolve(ContentRepository.class.getResource("/xsd/createReportInput.xsd"));
this.reportInputSchema = createSchema(new Source[] { source }, new ClassPathResourceResolver("/xsd"));
}
return this.reportInputSchema;
}
/**
* Erzeugt ein Schema auf Basis der übegebenen URIs
*

View file

@ -19,6 +19,7 @@ package de.kosit.validationtool.impl;
import static de.kosit.validationtool.impl.DateFactory.createTimestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@ -42,6 +43,7 @@ import de.kosit.validationtool.impl.tasks.ScenarioSelectionAction;
import de.kosit.validationtool.impl.tasks.SchemaValidationAction;
import de.kosit.validationtool.impl.tasks.SchematronValidationAction;
import de.kosit.validationtool.impl.tasks.ValidateReportInputAction;
import de.kosit.validationtool.impl.xml.ProcessorProvider;
import de.kosit.validationtool.model.reportInput.CreateReportInput;
import de.kosit.validationtool.model.reportInput.EngineType;
import de.kosit.validationtool.model.reportInput.XMLSyntaxError;
@ -61,31 +63,36 @@ public class DefaultCheck implements Check {
private final ConversionService conversionService;
@Getter
private final Configuration configuration;
private final List<Configuration> configuration;
@Getter
private final List<CheckAction> checkSteps;
@Getter
private final Processor processor;
public DefaultCheck(final Configuration... configuration) {
this(ProcessorProvider.getProcessor(), configuration);
}
/**
* Creates a new instance for the {@link Configuration}.
*
* @param configuration the Configuration
*/
public DefaultCheck(final Configuration configuration) {
this.configuration = configuration;
final ContentRepository content = configuration.getContentRepository();
final Processor processor = content.getProcessor();
public DefaultCheck(final Processor processor, final Configuration... configuration) {
this.configuration = Arrays.asList(configuration);
this.processor = processor;
this.conversionService = new ConversionService();
this.checkSteps = new ArrayList<>();
this.checkSteps.add(new DocumentParseAction(processor));
this.checkSteps.add(new CreateDocumentIdentificationAction());
this.checkSteps.add(new ScenarioSelectionAction(new ScenarioRepository(configuration)));
this.checkSteps.add(new SchemaValidationAction(content.getResolvingConfigurationStrategy(), processor));
this.checkSteps.add(new SchematronValidationAction(content.getResolver(), this.conversionService));
this.checkSteps.add(new ValidateReportInputAction(this.conversionService, content.getReportInputSchema()));
this.checkSteps.add(
new CreateReportAction(processor, this.conversionService, content.getResolver(), content.getUnparsedTextURIResolver()));
this.checkSteps.add(new SchemaValidationAction(processor));
this.checkSteps.add(new SchematronValidationAction(this.conversionService));
this.checkSteps.add(new ValidateReportInputAction(this.conversionService, SchemaProvider.getReportInputSchema()));
this.checkSteps.add(new CreateReportAction(processor, this.conversionService));
this.checkSteps.add(new ComputeAcceptanceAction());
}
@ -125,8 +132,7 @@ public class DefaultCheck implements Check {
}
private Result createResult(final Bag t) {
final DefaultResult result = new DefaultResult(t.getReport(), t.getAcceptStatus(),
new HtmlExtractor(this.configuration.getContentRepository().getProcessor()));
final DefaultResult result = new DefaultResult(t.getReport(), t.getAcceptStatus(), new HtmlExtractor(this.processor));
result.setWellformed(t.getParserResult().isValid());
result.setReportInput(t.getReportInput());
if (t.getSchemaValidationResult() != null) {

View file

@ -50,4 +50,20 @@ public class Printer {
public static void writeErr(final String message, final Object... params) {
System.err.println(new MessageFormat(message, Locale.ENGLISH).format(params));
}
/**
* Writes to standard error channel and prints a stacktrace.
*
* @param ex the exception
* @param message the message with placeholders
* @param params the params
*/
@SuppressWarnings("squid:S1148")
public static void writeErr(final Exception ex, final String message, final Object... params) {
writeErr(message, params);
if (ex != null) {
ex.printStackTrace();
}
}
}

View file

@ -20,6 +20,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Optional;
import javax.xml.transform.URIResolver;
import javax.xml.validation.Schema;
import lombok.AllArgsConstructor;
@ -27,9 +28,11 @@ import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import de.kosit.validationtool.api.ResolvingConfigurationStrategy;
import de.kosit.validationtool.model.scenarios.ResourceType;
import de.kosit.validationtool.model.scenarios.ScenarioType;
import net.sf.saxon.lib.UnparsedTextURIResolver;
import net.sf.saxon.s9api.XPathExecutable;
import net.sf.saxon.s9api.XPathSelector;
import net.sf.saxon.s9api.XsltExecutable;
@ -65,6 +68,12 @@ public class Scenario {
private XPathExecutable acceptExecutable;
private ResolvingConfigurationStrategy factory;
private URIResolver uriResolver;
private UnparsedTextURIResolver unparsedTextURIResolver;
@Setter
private List<Transformation> schematronValidations;

View file

@ -16,6 +16,7 @@
package de.kosit.validationtool.impl;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@ -38,20 +39,30 @@ import net.sf.saxon.s9api.XdmNode;
public class ScenarioRepository {
private final Configuration configuration;
public static final String DEFAULT = "default";
public ScenarioRepository(final Configuration configuration) {
this.configuration = configuration;
log.info("Loaded scenarios for {} by {} from {}. The following scenarios are available:\n\n{}", configuration.getName(),
configuration.getAuthor(), configuration.getDate(), summarizeScenarios());
public static final String DEFAULT_ID = DEFAULT + "_1";
private final List<Configuration> configuration;
public ScenarioRepository(final Configuration... configuration) {
if (configuration.length == 0) {
throw new IllegalArgumentException("Must provide at least one configuration");
}
this.configuration = Arrays.asList(configuration);
this.configuration.forEach(v -> log.info("Loaded scenarios for {} by {} from {}.", v.getName(), v.getAuthor(), v.getDate()));
log.info("The following scenarios are available:\n{}", summarizeScenarios());
}
public Scenario getFallbackScenario() {
return this.configuration.getFallbackScenario();
if (this.configuration.size() > 1) {
log.warn("Multiple configurations found. Using fallback scenario from first configuration");
}
return this.configuration.get(0).getFallbackScenario();
}
public List<Scenario> getScenarios() {
return this.configuration.getScenarios();
return this.configuration.stream().flatMap(c -> c.getScenarios().stream()).collect(Collectors.toList());
}
private String summarizeScenarios() {

View file

@ -0,0 +1,87 @@
/*
* Copyright 2017-2020 Koordinierungsstelle für IT-Standards (KoSIT)
*
* Licensed 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 de.kosit.validationtool.impl;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.w3c.dom.ls.LSResourceResolver;
import org.xml.sax.SAXException;
import de.kosit.validationtool.impl.xml.ClassPathResourceResolver;
/**
* @author Andreas Penski
*/
public class SchemaProvider {
private static Schema reportInputSchema;
/**
* Liefert das definierte Schema für die Validierung des [@link CreateReportInput}
*
* @return ReportInput-Schema
*/
public static Schema getReportInputSchema() {
if (reportInputSchema == null) {
final SchemaFactory sf = ResolvingMode.STRICT_RELATIVE.getStrategy().createSchemaFactory();
final Source source = resolve(SchemaProvider.class.getResource("/xsd/createReportInput.xsd"));
reportInputSchema = createSchema(sf, new Source[] { source }, new ClassPathResourceResolver("/xsd"));
}
return reportInputSchema;
}
private static Schema createSchema(final SchemaFactory sf, final Source[] schemaSources, final LSResourceResolver resourceResolver) {
try {
sf.setResourceResolver(resourceResolver);
return sf.newSchema(schemaSources);
} catch (final SAXException e) {
throw new IllegalArgumentException("Can not load schema from sources " + schemaSources[0].getSystemId(), e);
}
}
private static Schema createSchema(final SchemaFactory sf, final Source... schemaSources) {
return createSchema(sf, schemaSources, null);
}
@SuppressWarnings("java:S2095") // xml stack requires not closing the resource here
private static Source resolve(final URL resource) {
try {
final String rawPath = resource.toURI().getRawPath();
return new StreamSource(resource.openStream(), rawPath);
} catch (final IOException | URISyntaxException e) {
throw new IllegalStateException("Can not load schema for resource " + resource.getPath(), e);
}
}
/**
* Liefert das definiert Schema für die Szenario-Konfiguration
*
* @return Scenario-Schema
*/
public static Schema getScenarioSchema() {
final SchemaFactory sf = ResolvingMode.STRICT_RELATIVE.getStrategy().createSchemaFactory();
return createSchema(sf, resolve(SchemaProvider.class.getResource("/xsd/scenarios.xsd")));
}
}

View file

@ -23,7 +23,6 @@ import java.util.stream.Collectors;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.util.JAXBSource;
import javax.xml.transform.URIResolver;
import org.xml.sax.ContentHandler;
import org.xml.sax.DTDHandler;
@ -45,7 +44,6 @@ import de.kosit.validationtool.impl.EngineInformation;
import de.kosit.validationtool.impl.Scenario;
import de.kosit.validationtool.model.reportInput.XMLSyntaxError;
import net.sf.saxon.lib.UnparsedTextURIResolver;
import net.sf.saxon.s9api.BuildingContentHandler;
import net.sf.saxon.s9api.DocumentBuilder;
import net.sf.saxon.s9api.Processor;
@ -172,10 +170,6 @@ public class CreateReportAction implements CheckAction {
private final ConversionService conversionService;
private final URIResolver resolver;
private final UnparsedTextURIResolver unparsedTextURIResolver;
private static XsltExecutable loadFromScenario(final Scenario object) {
return object.getReportTransformation().getExecutable();
}
@ -198,9 +192,11 @@ public class CreateReportAction implements CheckAction {
transformer.setInitialContextNode(root);
final CollectingErrorEventHandler e = new CollectingErrorEventHandler();
transformer.setMessageListener(e);
transformer.setURIResolver(this.resolver);
if (this.unparsedTextURIResolver != null) {
transformer.getUnderlyingController().setUnparsedTextURIResolver(this.unparsedTextURIResolver);
final Scenario scenario = results.getScenarioSelectionResult().getObject();
transformer.setURIResolver(scenario.getUriResolver());
if (scenario.getUnparsedTextURIResolver() != null) {
transformer.getUnderlyingController().setUnparsedTextURIResolver(scenario.getUnparsedTextURIResolver());
}
if (parsedDocument != null) {
transformer.setParameter(new QName("input-document"), parsedDocument);

View file

@ -38,7 +38,6 @@ import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import de.kosit.validationtool.api.Input;
import de.kosit.validationtool.api.ResolvingConfigurationStrategy;
import de.kosit.validationtool.impl.CollectingErrorEventHandler;
import de.kosit.validationtool.impl.Scenario;
import de.kosit.validationtool.impl.input.AbstractInput;
@ -134,8 +133,6 @@ public class SchemaValidationAction implements CheckAction {
private static final String LIMIT_PARAMETER = "schema.validation.inmem.limit";
private final ResolvingConfigurationStrategy factory;
private final Processor processor;
@Setter(AccessLevel.PACKAGE)
@ -147,7 +144,7 @@ public class SchemaValidationAction implements CheckAction {
final CollectingErrorEventHandler errorHandler = new CollectingErrorEventHandler();
try ( final SourceProvider validateInput = resolveSource(results) ) {
final Validator validator = this.factory.createValidator(scenario.getSchema());
final Validator validator = scenario.getFactory().createValidator(scenario.getSchema());
validator.setErrorHandler(errorHandler);
validator.validate(validateInput.getSource());
return new Result<>(!errorHandler.hasErrors(), errorHandler.getErrors());

View file

@ -19,7 +19,6 @@ package de.kosit.validationtool.impl.tasks;
import java.util.List;
import java.util.stream.Collectors;
import javax.xml.transform.URIResolver;
import javax.xml.transform.dom.DOMSource;
import org.oclc.purl.dsdl.svrl.SchematronOutput;
@ -30,6 +29,7 @@ import lombok.extern.slf4j.Slf4j;
import de.kosit.validationtool.impl.CollectingErrorEventHandler;
import de.kosit.validationtool.impl.ConversionService;
import de.kosit.validationtool.impl.Scenario;
import de.kosit.validationtool.impl.Scenario.Transformation;
import de.kosit.validationtool.model.reportInput.CreateReportInput;
import de.kosit.validationtool.model.reportInput.ValidationResultsSchematron;
import de.kosit.validationtool.model.reportInput.ValidationResultsSchematron.Results;
@ -49,21 +49,20 @@ import net.sf.saxon.s9api.XsltTransformer;
@Slf4j
public class SchematronValidationAction implements CheckAction {
private final URIResolver resolver;
private final ConversionService conversionService;
private List<ValidationResultsSchematron> validate(final Bag results, final XdmNode document, final Scenario scenario) {
return scenario.getSchematronValidations().stream().map(v -> validate(results, document, v)).collect(Collectors.toList());
return scenario.getSchematronValidations().stream().map(v -> validate(scenario, results, document, v)).collect(Collectors.toList());
}
private ValidationResultsSchematron validate(final Bag results, final XdmNode document, final Scenario.Transformation validation) {
private ValidationResultsSchematron validate(final Scenario scenario, final Bag results, final XdmNode document,
final Transformation validation) {
final ValidationResultsSchematron s = new ValidationResultsSchematron();
s.setResource(validation.getResourceType());
try {
final XsltTransformer transformer = validation.getExecutable().load();
// resolving nur relative zum Repository
transformer.setURIResolver(this.resolver);
transformer.setURIResolver(scenario.getUriResolver());
final CollectingErrorEventHandler e = new CollectingErrorEventHandler();
transformer.setMessageListener(e);

View file

@ -28,34 +28,14 @@ import lombok.extern.slf4j.Slf4j;
import de.kosit.validationtool.api.ResolvingConfigurationStrategy;
import net.sf.saxon.s9api.Processor;
/**
* @author Andreas Penski
*/
@Slf4j
public abstract class BaseResolvingStrategy implements ResolvingConfigurationStrategy {
protected static final String DISSALLOW_DOCTYPE_DECL_FEATURE = "http://apache.org/xml/features/disallow-doctype-decl";
protected static final String LOAD_EXTERNAL_DTD_FEATURE = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
protected static final String FEATURE_SECURE_PROCESSING = "http://javax.xml.XMLConstants/feature/secure-processing";
private static final String ORACLE_XERCES_CLASS = "com.sun.org.apache.xerces.internal.impl.Constants";
private Processor processor;
@Override
public Processor getProcessor() {
if (this.processor == null) {
this.processor = createProcessor();
}
return this.processor;
}
protected abstract Processor createProcessor();
public static void forceOpenJdkXmlImplementation() {
if (!isOpenJdkXmlImplementationAvailable()) {
throw new IllegalStateException("No OpenJDK version of XERCES found");

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package de.kosit.validationtool.impl;
package de.kosit.validationtool.impl.xml;
import java.io.IOException;
import java.io.InputStream;
@ -31,15 +31,13 @@ import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import de.kosit.validationtool.impl.xml.RelativeUriResolver;
/**
* {@link LSResourceResolver} der objekte relativ zu einem Basis-Pfad aus dem Classpath der Anwendung laden kann.
*
* @author Andreas Penski
*/
@Slf4j
class ClassPathResourceResolver implements LSResourceResolver {
public class ClassPathResourceResolver implements LSResourceResolver {
/**
* Simple {@link LSInput}-Implementierung, die einen Stream liefern kann

View file

@ -0,0 +1,117 @@
/*
* Copyright 2017-2020 Koordinierungsstelle für IT-Standards (KoSIT)
*
* Licensed 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 de.kosit.validationtool.impl.xml;
import java.io.Reader;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import javax.xml.XMLConstants;
import javax.xml.transform.Result;
import javax.xml.transform.TransformerException;
import lombok.SneakyThrows;
import net.sf.saxon.Configuration;
import net.sf.saxon.expr.XPathContext;
import net.sf.saxon.lib.CollectionFinder;
import net.sf.saxon.lib.Feature;
import net.sf.saxon.lib.FeatureKeys;
import net.sf.saxon.lib.OutputURIResolver;
import net.sf.saxon.lib.ResourceCollection;
import net.sf.saxon.lib.UnparsedTextURIResolver;
import net.sf.saxon.s9api.Processor;
import net.sf.saxon.trans.XPathException;
/**
* @author Andreas Penski
*/
public class ProcessorProvider {
private static class SecureUriResolver implements CollectionFinder, OutputURIResolver, UnparsedTextURIResolver {
public static final String MESSAGE = "Configuration error. Resolving ist not allowed";
@Override
public OutputURIResolver newInstance() {
return this;
}
@Override
public Result resolve(final String href, final String base) throws TransformerException {
throw new IllegalStateException(MESSAGE);
}
@Override
public void close(final Result result) throws TransformerException {
throw new IllegalStateException(MESSAGE);
}
@Override
public Reader resolve(final URI absoluteURI, final String encoding, final Configuration config) throws XPathException {
throw new IllegalStateException(MESSAGE);
}
@Override
public ResourceCollection findCollection(final XPathContext context, final String collectionURI) throws XPathException {
throw new IllegalStateException(MESSAGE);
}
}
protected static final String DISSALLOW_DOCTYPE_DECL_FEATURE = "http://apache.org/xml/features/disallow-doctype-decl";
protected static final String LOAD_EXTERNAL_DTD_FEATURE = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
protected static final String FEATURE_SECURE_PROCESSING = "http://javax.xml.XMLConstants/feature/secure-processing";
private static Processor processor;
@SneakyThrows
private static String encode(final String input) {
return URLEncoder.encode(input, StandardCharsets.UTF_8.name());
}
public static Processor getProcessor() {
if (processor == null) {
processor = createProcessor();
}
return processor;
}
private static Processor createProcessor() {
final Processor processor = new Processor(false);
// verhindere global im Prinzip alle resolving strategien
final SecureUriResolver resolver = new SecureUriResolver();
processor.getUnderlyingConfiguration().setCollectionFinder(resolver);
processor.getUnderlyingConfiguration().setOutputURIResolver(resolver);// NOSONAR
processor.getUnderlyingConfiguration().setUnparsedTextURIResolver(resolver);
// grundsätzlich Feature-konfiguration:
processor.setConfigurationProperty(Feature.DTD_VALIDATION, false);
processor.setConfigurationProperty(Feature.ENTITY_RESOLVER_CLASS, "");
processor.setConfigurationProperty(Feature.XINCLUDE, false);
processor.setConfigurationProperty(Feature.ALLOW_EXTERNAL_FUNCTIONS, false);
// Konfiguration des zu verwendenden Parsers, wenn Saxon selbst einen erzeugen muss, bspw. beim XSL parsen
processor.setConfigurationProperty(FeatureKeys.XML_PARSER_FEATURE + encode(FEATURE_SECURE_PROCESSING), true); // NOSONAR
processor.setConfigurationProperty(FeatureKeys.XML_PARSER_FEATURE + encode(DISSALLOW_DOCTYPE_DECL_FEATURE), true);// NOSONAR
processor.setConfigurationProperty(FeatureKeys.XML_PARSER_FEATURE + encode(LOAD_EXTERNAL_DTD_FEATURE), false);// NOSONAR
processor.setConfigurationProperty(FeatureKeys.XML_PARSER_FEATURE + encode(XMLConstants.ACCESS_EXTERNAL_DTD), false);// NOSONAR
return processor;
}
}

View file

@ -16,32 +16,17 @@
package de.kosit.validationtool.impl.xml;
import java.io.Reader;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import javax.xml.XMLConstants;
import javax.xml.transform.Result;
import javax.xml.transform.TransformerException;
import javax.xml.transform.URIResolver;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import net.sf.saxon.Configuration;
import net.sf.saxon.expr.XPathContext;
import net.sf.saxon.lib.CollectionFinder;
import net.sf.saxon.lib.Feature;
import net.sf.saxon.lib.FeatureKeys;
import net.sf.saxon.lib.OutputURIResolver;
import net.sf.saxon.lib.ResourceCollection;
import net.sf.saxon.lib.UnparsedTextURIResolver;
import net.sf.saxon.s9api.Processor;
import net.sf.saxon.trans.XPathException;
/**
* @author Andreas Penski
@ -49,78 +34,16 @@ import net.sf.saxon.trans.XPathException;
@RequiredArgsConstructor
public class StrictRelativeResolvingStrategy extends BaseResolvingStrategy {
private static class SecureUriResolver implements CollectionFinder, OutputURIResolver, UnparsedTextURIResolver {
public static final String MESSAGE = "Configuration error. Resolving ist not allowed";
@Override
public OutputURIResolver newInstance() {
return this;
}
@Override
public Result resolve(final String href, final String base) throws TransformerException {
throw new IllegalStateException(MESSAGE);
}
@Override
public void close(final Result result) throws TransformerException {
throw new IllegalStateException(MESSAGE);
}
@Override
public Reader resolve(final URI absoluteURI, final String encoding, final Configuration config) throws XPathException {
throw new IllegalStateException(MESSAGE);
}
@Override
public ResourceCollection findCollection(final XPathContext context, final String collectionURI) throws XPathException {
throw new IllegalStateException(MESSAGE);
}
}
/**
* e.g. don't allow any scheme
*/
private static final String EMPTY_SCHEME = "";
@Override
public SchemaFactory createSchemaFactory() {
forceOpenJdkXmlImplementation();
@SuppressWarnings("java:S2755") //
final SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
disableExternalEntities(sf);
allowExternalSchema(sf, "file");
return sf;
}
@Override
protected Processor createProcessor() {
final Processor processor = new Processor(false);
// verhindere global im Prinzip alle resolving strategien
final SecureUriResolver resolver = new SecureUriResolver();
processor.getUnderlyingConfiguration().setCollectionFinder(resolver);
processor.getUnderlyingConfiguration().setOutputURIResolver(resolver);// NOSONAR
processor.getUnderlyingConfiguration().setUnparsedTextURIResolver(resolver);
// grundsätzlich Feature-konfiguration:
processor.setConfigurationProperty(Feature.DTD_VALIDATION, false);
processor.setConfigurationProperty(Feature.ENTITY_RESOLVER_CLASS, "");
processor.setConfigurationProperty(Feature.XINCLUDE, false);
processor.setConfigurationProperty(Feature.ALLOW_EXTERNAL_FUNCTIONS, false);
// Konfiguration des zu verwendenden Parsers, wenn Saxon selbst einen erzeugen muss, bspw. beim XSL parsen
processor.setConfigurationProperty(FeatureKeys.XML_PARSER_FEATURE + encode(FEATURE_SECURE_PROCESSING), true); // NOSONAR
processor.setConfigurationProperty(FeatureKeys.XML_PARSER_FEATURE + encode(DISSALLOW_DOCTYPE_DECL_FEATURE), true);// NOSONAR
processor.setConfigurationProperty(FeatureKeys.XML_PARSER_FEATURE + encode(LOAD_EXTERNAL_DTD_FEATURE), false);// NOSONAR
processor.setConfigurationProperty(FeatureKeys.XML_PARSER_FEATURE + encode(XMLConstants.ACCESS_EXTERNAL_DTD), false);// NOSONAR
return processor;
}
@SneakyThrows
private static String encode(final String input) {
return URLEncoder.encode(input, StandardCharsets.UTF_8.name());
}
@Override
public URIResolver createResolver(final URI repositoryURI) {
return new RelativeUriResolver(repositoryURI);