From 649ea6c0c4f61105fdb53b6876e0951c1979f50e Mon Sep 17 00:00:00 2001 From: "Andreas Penski (init)" Date: Wed, 6 May 2020 10:49:12 +0200 Subject: [PATCH] documentation --- README.md | 15 ++- docs/api.md | 93 +++++++++++++++++++ .../kosit/validationtool/config/Builder.java | 10 +- .../config/ConfigurationBuilder.java | 58 +++++++++++- .../validationtool/impl/ResolvingMode.java | 3 +- 5 files changed, 174 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 483ba8b..ee08010 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ The general way using the CLI is: java -jar validationtool--standalone.jar -s [OPTIONS] [FILE] [FILE] [FILE] ... ``` -You can more CLI options by +You can see more CLI options with ```shell java -jar validationtool--standalone.jar --help @@ -68,7 +68,18 @@ A concrete example with a specific validator configuration can be found on [GitH ### Application User Interface (API / embedded usage) -The validator can also be used in own Java Applications via the API. Details can be [found here](./docs/api.md). +The validator can also be used in own Java Applications via the API. Usage would be something like this: +``` +Path scenarios = Paths.get("scenarios.xml"); +Configuration config = Configuration.load(scenarios.toUri()); +Input document = InputFactory.read(testDocument); + +Check validator = new DefaultCheck(config); +Result validationResult = validator.checkInput(document); + +// examine the result here +``` +Details and further configuration options can be [found here](./docs/api.md). ### Daemon-Mode diff --git a/docs/api.md b/docs/api.md index e00f0ea..c59bc23 100644 --- a/docs/api.md +++ b/docs/api.md @@ -121,3 +121,96 @@ recommendation. This allows to have control over what validation result is to be considered _acceptable_ for your own application context. E.g. you can overrule schematron validation errors with _acceptMatch_ configuration and consider certain errors as _acceptable_. Nevertheless you can *not* overrule schema errors with accept match. + +## Building scenario configurations with the Builder API +Despite using preconfigured [scenario files](configurations.md) it is also possible to create a validator configuration using +a builder API. A valid configuration consists of the following: + +* at least one valid scenario configuration, this includes + * a valid match configuration to identify/activate this scenario + * a valid XML schema configuration + * a valid report transformation configuration + * valid schematron validation configurations (optional) + * a valid accept match configuration to compute acceptance information (optional) +* valid a fallback scenario configuration + +A simple configuration looks like this: + +```java +import static de.kosit.validationtool.config.ConfigurationBuilder.*; +import de.kosit.validationtool.api.Configuration; +import java.net.URI; +import java.nio.file.Path; + +public class MyValidator { + + public static void main(String[] args) { + Configuration config = Configuration.create().name("myconfiguration") + .with(scenario("firstScenario") + .match("//myNode") + .validate(schema("Sample Schema").schemaLocation(URI.create("simple.xsd"))) + .validate(schematron("my rules").source("myRules.xsl")) + .with(report("my report").source("report.xsl"))) + .with(fallback().name("default-report").source("fallback.xsl")) + .useRepository(Paths.get("/opt/myrepository")) + .build(); + Check validator = new DefaultCheck(config); + // .. run your checks + } +} +``` + +There a various methods provided by the builder API to configure your scenarios and the validation process. + +It is also possible to provide runtime artifacts like `XsltExecutable`, `XPathExecutalbe` or `Schema` to configure the validator. +This gives you complete control how to load these artifacts. +--- +**Note:** Creating this objects requires usage of the same instance of the saxon `Processor` as used during validation later. So you +need to supply a custom `ResolvingConfigurationStrategy` or use the internal one to create these objects. See below. + +--- +## Configure xml security an resolving + +When using xml related technologies you are supposed to handle certein security issues properly. The KoSIT validator pursues a rather strict +strategy. The default configuration + +* disable DTD validation completely +* allows loading/resolving only from a configured local content repository (a specific folder) +* tries to prevent known XML security issues (see [OWASP XML_Security_Cheat_Sheet.html](https://cheatsheetseries.owasp.org/cheatsheets/XML_Security_Cheat_Sheet.html)) +* only works with OpenJDK based XML stacks + +However, you can configure certain aspects related to resolving and security yourself. The validator uses a single interface for accessing +or creating the neccessary XML API objects like `SchemaFactory`, `Validator`,`URIResolver` or `Processor`: [ResolvingConfigurationStrategy.java](https://github.com/itplr-kosit/validator/tree/master/src/main/java/de/kosit/validationtool/api/ResolvingConfigurationStrategy.java) + +There are 3 implemenations available out of the box: + +1. [StrictRelativeResolvingStrategy.java](https://github.com/itplr-kosit/validator/tree/master/src/main/java/de/kosit/validationtool/impl/xml/StrictRelativeResolvingStrategy.java) +which is the **default**, prevents known XML attacks and only allows loading from a specific local repository location +1. [StrictLocalResolvingStrategy.java](https://github.com/itplr-kosit/validator/tree/master/src/main/java/de/kosit/validationtool/impl/xml/StrictLocalResolvingStrategy.java) +which opens the first to load resource from local location +1. [RemoteResolvingStrategy.java](https://github.com/itplr-kosit/validator/tree/master/src/main/java/de/kosit/validationtool/impl/xml/RemoteResolvingStrategy.java) +which opens the first to load resource also from remote locations via http and https + +You can configure usage of one of this implemenations via + +````java +Conifuguration config = Configuration.load(URI.create("myscenarios.xml")) + .resolvingMode(ResolvingMode.STRICT_LOCAL) + .build(); +```` + +If you decide to implement your own strategy you can configure this via: + +````java +Conifuguration config = Configuration.load(URI.create("myscenarios.xml")) + .resolvingStrategy(new MyCustomResolvingConfigurationStrategy()) + .build(); +```` + +--- +**Attention:** If you decide to implement a custom strategy you need to handle xml security risk. Please make sure, that you prevent XXE attacks and +other kind of attacks. Consider using [BaseResolvingStrategy.java](https://github.com/itplr-kosit/validator/tree/master/src/main/java/de/kosit/validationtool/impl/xml/BaseResolvingStrategy.java) +and the protected methods within to disable certain features. + +--- + \ No newline at end of file diff --git a/src/main/java/de/kosit/validationtool/config/Builder.java b/src/main/java/de/kosit/validationtool/config/Builder.java index bdee7fd..6182e30 100644 --- a/src/main/java/de/kosit/validationtool/config/Builder.java +++ b/src/main/java/de/kosit/validationtool/config/Builder.java @@ -4,9 +4,17 @@ import de.kosit.validationtool.impl.ContentRepository; import de.kosit.validationtool.impl.model.Result; /** + * Internal interface for creating object builders. + * * @author Andreas Penski */ -public interface Builder { +interface Builder { + /** + * Creates an object based on artifacts provided via a defined {@link ContentRepository}. + * + * @param repository the {@link ContentRepository} + * @return the result of building the object + */ Result build(ContentRepository repository); } diff --git a/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java b/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java index fab99b4..ce199b3 100644 --- a/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java +++ b/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java @@ -3,6 +3,7 @@ package de.kosit.validationtool.config; import static de.kosit.validationtool.impl.DateFactory.createTimestamp; import java.net.URI; +import java.nio.file.Path; import java.time.LocalDate; import java.util.ArrayList; import java.util.Date; @@ -75,6 +76,12 @@ public class ConfigurationBuilder { return this; } + /** + * Add a specific nam to this configuration + * + * @param name the name of the configuration + * @return this + */ public ConfigurationBuilder name(final String name) { this.name = name; return this; @@ -103,11 +110,25 @@ public class ConfigurationBuilder { return date(date != null ? LocalDate.ofEpochDay(date.getTime()) : null); } + /** + * Adds a {@link Scenario} to this list of know scenarios. Note: order of calling this methods defines order of + * scenarios when determining the target scenario for a given xml file. + * + * @param scenarioBuilder the {@link ScenarioBuilder} building the {@link Scenario} + * @return this + */ public ConfigurationBuilder with(final ScenarioBuilder scenarioBuilder) { this.scenarios.add(scenarioBuilder); return this; } + /** + * Sets a specific fallback scenario configuration. Note: calling this more than once is possible, but the last call + * will define the actual fallback scenario used. There can be only one + * + * @param builder the {@link FallbackBuilder} + * @return this + */ public ConfigurationBuilder with(final FallbackBuilder builder) { if (this.fallbackBuilder != null) { log.warn("Overriding previously created fallback scenario"); @@ -116,6 +137,12 @@ public class ConfigurationBuilder { return this; } + /** + * Adds a description to this configuration. + * + * @param description the descriptioin + * @return this + */ public ConfigurationBuilder description(final String description) { this.description = description; return this; @@ -219,6 +246,12 @@ public class ConfigurationBuilder { return new ReportBuilder().name(name); } + /** + * Builds the actual {@link Configuration} by validating all builder inputs and constructing neccessary objects. + * + * @return a valid configuration + * @throws IllegalStateException when the configuration is not valid/complete + */ public Configuration build() { final ResolvingConfigurationStrategy resolving = getResolvingConfigurationStrategy(); if (this.processor == null) { @@ -269,7 +302,7 @@ public class ConfigurationBuilder { } private List initializeScenarios(final ContentRepository contentRepository) { - if (this.scenarios.size() == 0) { + if (this.scenarios.isEmpty()) { throw new IllegalStateException("No scenario specified"); } return this.scenarios.stream().map(s -> { @@ -291,6 +324,13 @@ public class ConfigurationBuilder { return this.resolvingMode.getStrategy(); } + /** + * Sets a specific resolving mode, for resolving xml artifacts for this configuration. See {@link ResolvingMode} for + * details. + * + * @param mode the mode + * @return this + */ public ConfigurationBuilder resolvingMode(final ResolvingMode mode) { this.resolvingMode = mode; return this; @@ -307,8 +347,24 @@ public class ConfigurationBuilder { return this; } + /** + * Set a specific repository location for resolving artifacts for scenarios. + * + * @param repository the repository location + * @return this + */ public ConfigurationBuilder useRepository(final URI repository) { this.repository = repository; return this; } + + /** + * Set a specific repository location for resolving artifacts for scenarios. + * + * @param repository the repository location + * @return this + */ + public ConfigurationBuilder useRepository(final Path repository) { + return useRepository(repository.toUri()); + } } diff --git a/src/main/java/de/kosit/validationtool/impl/ResolvingMode.java b/src/main/java/de/kosit/validationtool/impl/ResolvingMode.java index 885674b..1ca8517 100644 --- a/src/main/java/de/kosit/validationtool/impl/ResolvingMode.java +++ b/src/main/java/de/kosit/validationtool/impl/ResolvingMode.java @@ -4,6 +4,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; import de.kosit.validationtool.api.ResolvingConfigurationStrategy; +import de.kosit.validationtool.impl.xml.RemoteResolvingStrategy; import de.kosit.validationtool.impl.xml.StrictLocalResolvingStrategy; import de.kosit.validationtool.impl.xml.StrictRelativeResolvingStrategy; @@ -24,7 +25,7 @@ public enum ResolvingMode { STRICT_LOCAL(new StrictLocalResolvingStrategy()), - JDK_SUPPORTED(null), + ALLOW_REMOTE(new RemoteResolvingStrategy()), CUSTOM(null);