Skip to content

Add sealed CI integration: a GitHub Action and a GitLab CI template - #182

Open
gronke wants to merge 1 commit into
itplr-kosit:mainfrom
gronke:feature/sealed-ci
Open

Add sealed CI integration: a GitHub Action and a GitLab CI template#182
gronke wants to merge 1 commit into
itplr-kosit:mainfrom
gronke:feature/sealed-ci

Conversation

@gronke

@gronke gronke commented Jul 23, 2026

Copy link
Copy Markdown

This adds first-class CI support for the validator, so any project can validate its XML documents against a validation configuration as part of a pipeline, using the released, unmodified validator jar.

- uses: itplr-kosit/validator@main
  with:
    version: "1.6.2"
    configuration: "https://github.com/itplr-kosit/validator-configuration-xrechnung/releases/download/v2026-01-31/xrechnung-3.0.2-validator-configuration-2026-01-31.zip"
    files: invoices/

One engine, two platforms

scripts/validate.sh contains the whole logic once: download the released standalone jar and the configuration, verify them against optional SHA-256 pins, validate every document, print PASS/FAIL per file with the failing rule ids, fail the job on any rejection, and expose the reports plus accepted/rejected counts. The GitHub Action (action.yml) is a thin composite mapping its inputs onto the script's environment; the GitLab template (gitlab/validator.gitlab-ci.yml) fetches the same script from this repository at KOSIT_SCRIPT_REF and drives it with the same variables. Both platforms therefore run identical, independently reviewable logic; there is no second copy to keep in sync.

Sealed execution

Validators process untrusted documents, and CI machines hold credentials.
The action therefore executes the validator inside a container when Docker is available (the sealed input: auto by default, always, never): an eclipse-temurin:21-jre container with no network access, a read-only root filesystem, all capabilities dropped and the caller's uid. Only the jar, the configuration and a scratch directory are mounted. Downloads always happen outside the container, so the validator itself never touches the network. With sealed: always the host JVM setup is skipped entirely.

Runtime policy: pin the contract, float the patch level

The released jars are the checksummed artifacts; the container image is a security-patch channel. The default image tag eclipse-temurin:21-jre pins the Java 21 JRE contract and floats the patch level, so JRE and OS security updates arrive without a change to this repository. That matters for a repository that does not receive frequent maintenance commits.
Consumers with a stricter supply-chain policy pass a digest-pinned reference through the image input (KOSIT_IMAGE on GitLab). The GitLab job image is the same runtime, giving both platforms an identical execution environment.

Development and self-test

A .devcontainer/ provides the matching Maven and Temurin 21 setup for working on the validator itself. The action's self-test validates a conformant XRechnung reference instance from the official test suite and asserts that a corrupted copy is rejected, through the sealed path (default-image fallback and explicit image override) and the host JVM path.

Compatibility

No change to the Java build, the Maven artifacts or the release layout; everything added is additive (action.yml, scripts/, gitlab/, .devcontainer/, one README section).


#181 proposes a minimal variant of the same idea, though a compartmentalized approach using sealed Docker images is advisable. This requires Docker to be available in the runner, which is why I'd like to provide both alternatives for review and feedback.

@rkottmann
rkottmann requested review from phax and rkottmann July 23, 2026 15:22
@phax

phax commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

@gronke Is my interpretation correct, that this is a superset of #181?

@gronke

gronke commented Jul 24, 2026

Copy link
Copy Markdown
Author

Yes, #181 is simpler in sense of the Docker layer, which I personally find recommendable hardening. Why not lock down permissions, so that this can be safely trusted in a multi-staged job environment. For me a clear reason to prefer this one over 181, though I find it better documents the intention. Consider it the pitch of the idea and this here being the more elaborate version of it.

@phax

phax commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

Okay, thanks for the clarification. So I will answer only here, but it also applies to 181.

May I ask what your basic intention is? Do you want to use it to test the Validator development itself, or do you focus on using the Validator in other use cases that e.g. create XML documents that need validation?

We are currently focussing our development efforts on version 2 of the Validator. As this might change how the Validator is to be used in a CI we would like to postpone dealing with this PR until we have more clarity on V2.

So may I ask you to please leave this PR open and have some patience with us. Starting from September we should be able to re-assess your nice work. Until then, others can already benefit from it.

scripts/validate.sh is the engine behind both platforms: download the released standalone jar and the validation configuration (SHA-256-verified when pinned), validate each document, print PASS/FAIL with the failing rule ids and fail on any rejection.
The GitHub Action (action.yml) maps its inputs onto the script's environment; the GitLab template fetches the same script from this repository at KOSIT_SCRIPT_REF and drives it with the same variables, so both platforms run identical logic.
With Docker available the action executes each validation sealed (the "sealed" input, default "auto"): inside an eclipse-temurin:21-jre container without network access, a read-only root, no capabilities and no privilege escalation, run with an init process, a pids limit and the caller's uid; the document, the jar and the configuration are mounted read-only, only the report directory and a bounded /tmp tmpfs are writable.
Downloads always happen outside the container, so the validator itself never touches the network, and the host JVM setup is skipped entirely for "sealed: always".
The image tag pins the Java 21 JRE contract and floats the patch level: the released jars are the checksummed artifacts, the runtime is a security-patch channel; an "image" input (KOSIT_IMAGE on GitLab) accepts a digest-pinned reference for stricter policies.
The GitLab job image is the same runtime, giving both platforms an identical execution environment.
The action self-test validates a conformant XRechnung reference instance and asserts a corrupted one is rejected, exercising the sealed path through the empty-input fallback and an explicit image override, plus the host path.
@gronke
gronke force-pushed the feature/sealed-ci branch from ba93207 to 599042f Compare July 25, 2026 10:26
@gronke

gronke commented Jul 25, 2026

Copy link
Copy Markdown
Author

The latter - I develop library that produces EN 16931 / XRechnung documents, and its CI validates the generated corpus with the released, unmodified validator and the XRechnung configuration on every change.

To give you an example, this is the current CI configuration of that project:

conformance:
  name: Conformance (Mustang / KoSIT / veraPDF)
  runs-on: ubuntu-latest
  timeout-minutes: 20
  env:
    SEALED: "1"
  steps:
    - uses: actions/checkout@v7

    - uses: ./.github/actions/mustang-validate

    - name: Validate the corpora with the KoSIT validator (sealed)
      uses: gronke/fork-kosit-validator@ba93207b776962d57ad6ec9adb0739196fc206ae
      with:
        version: "1.6.2"
        checksum: 244978514ad48f67c7573acfffc8f4fd73d81feda6f276710033f9913579857e
        configuration: https://github.com/itplr-kosit/validator-configuration-xrechnung/releases/download/v2026-01-31/xrechnung-3.0.2-validator-configuration-2026-01-31.zip
        configuration-checksum: 6a5a5911a421b25fbc423f62f93f894df7b236f5d73ca4f84bb222a945082704
        files: crates/*/tests/corpus/*.xml
        sealed: always

    - name: Validate the hybrid PDF pipeline (sealed)
      run: bash scripts/validate-pdf.sh

At this moment my repository still vendors a custom GitHub Action for ZUGFeRD/mustangproject, which so far I haven't suggested upstream yet.

For a developer of a library it is useful to test against external and independently maintained test suites. Running external tools in a CI can be dangerous though, because it could leak environment secrets, private code, compromise following build steps or caches - and this is where the sealed Docker images are useful. They reduce the review effort to inspecting the Action, finding the containers drop privileges and allow to assess the remaining potential threat for the CI. By offering them, we allow users of this validator Action to keep focus on their code.

Beyond my very own use case for the Action, other folks may use ARC to produce documents with needs for validation.

We are currently focussing our development efforts on version 2 of the Validator. As this might change how the Validator is to be used in a CI we would like to postpone dealing with this PR until we have more clarity on V2.

I imagine the proposed usage of this action can't be that different from the proposal for v1. I would also not mind if you'd stop providing an action for a new major release.

check:
  uses: itplr-kosit/validator@v1
  files: /tmp/*.xml

This PR has more complexity than necessarily needed to get to a GitHub Action. I noticed you work on a GitLab environment, which is why I added a reusable template for GitLab as well. Maybe you even find that useful to run a series of test documents on v1 and v2 for a differential comparison of results and performance.

So may I ask you to please leave this PR open and have some patience with us. Starting from September we should be able to re-assess your nice work. Until then, others can already benefit from it.

Of course, I'll leave it on GitHub (and from my own project). Looking forward to hear more news from v2 in September.

@phax

phax commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the clarification. Without having it crosschecked with KoSIT, but I think they don't have the knowledge and resources to continuously provide a common Docker image, Github actions etc. So maybe this could be provided by you as a separate project and from within this project, we could link to your upstream projects then. I am convinced that this would be very benefitial for the community.
If this sounds interesting to you, we might have a short call to discuss details - so that you can also work independent from us.

@gronke

gronke commented Jul 25, 2026

Copy link
Copy Markdown
Author

In this case the maintenance of a GitHub Action is pretty straight forward. It runs a self-test, so that we could configure it to block cutting a new release of the action by accident with a broken version. It would fail noticeably on every PR.

What could complicate the situation is the different GitLab development and GitHub environments. But in the worst case failure surfaces on sync to GitHub, with the consequence that the GitHub Action is skipping an update until the build and selftest are green again.

What I think we should definitely do, if considering this route, is conducting more tests from a GitLab runner, because my work environment is on GitHub, where I would notice mismatch. Can do that testing, but wanted to check back whether you would even consider to feature a GitHub Action or GitLab CI Template.

So maybe this could be provided by you as a separate project and from within this project, we could link to your upstream projects then. I am convinced that this would be very beneficial for the community.

Sure, I will then vendor these from the project I'm currently working on and offer them from there. I doubt that many users will want to trust an additional party for validating their documents, but the code will be public and can be copied by whoever likes to.

We do not need to build or package Docker images, this runs on eclipse-temurin:21-jre. You would basically only offer the recipe for GitLab and GitHub to use the validator inside a Docker harness with well-defined input. If that input values change, the Action needs to change with it - breaking change will be noticed by the self-test.

I did not include an Action to to manage the Action's release cycle, which after hearing your feedback would be useful to have. (e.g. GitHub automatically cuts a new release tag when the default branch changed, unless the self-test fails, then it would flag the issue (and notify somebody?).

If this sounds interesting to you, we might have a short call to discuss details - so that you can also work independent from us.

Thanks for all the insight and your feedback 🤩 If you suggest to chat about this in a call, please feel free to send an email/invite to stefan@gronke.net - I'll be behind the screen for the next couple more hours.

@phax

phax commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

Thanks Stefan - won't happen today. I first need to discuss it with KoSIT and next week I am on vacation ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants