Files
govoplan-xrechnung/src/govoplan_xrechnung/backend/manifest.py
T
zemion b73709c65a fix(xrechnung): bound validator output and report reads
Enforce capture limits while draining both subprocess pipes, kill and reap interrupted validators, and bound report allocation before parsing. Preserve result precedence and add synthetic-process regressions with EN/DE documentation.

Refs #2
2026-09-08 05:32:37 +02:00

263 lines
19 KiB
Python

from __future__ import annotations
from govoplan_core.core.access import (
CAPABILITY_AUTH_PERMISSION_EVALUATOR,
CAPABILITY_AUTH_PRINCIPAL_RESOLVER,
)
from govoplan_core.core.modules import (
DocumentationCondition,
DocumentationLink,
DocumentationTopic,
ModuleManifest,
PermissionDefinition,
RoleTemplate,
)
from govoplan_core.core.provider_governance import declared_module_architecture
MODULE_ID = "xrechnung"
MODULE_VERSION = "0.1.21"
READ_SCOPE = "xrechnung:validation:read"
EXECUTE_SCOPE = "xrechnung:validation:execute"
HANDOFF_SCOPE = "xrechnung:handoff:create"
def _permission(scope: str, label: str, description: str) -> PermissionDefinition:
module_id, resource, action = scope.split(":", 2)
return PermissionDefinition(
scope=scope,
label=label,
description=description,
category="XRechnung",
level="tenant",
module_id=module_id,
resource=resource,
action=action,
)
manifest = ModuleManifest(
id=MODULE_ID,
name="XRechnung",
version=MODULE_VERSION,
dependencies=("access",),
optional_dependencies=("files", "procurement", "payments", "records", "audit"),
required_capabilities=(
CAPABILITY_AUTH_PRINCIPAL_RESOLVER,
CAPABILITY_AUTH_PERMISSION_EVALUATOR,
),
permissions=(
_permission(READ_SCOPE, "View XRechnung validation", "Read validation profiles, outcomes, and non-secret diagnostics."),
_permission(EXECUTE_SCOPE, "Validate inbound XRechnung", "Run a pinned local KoSIT profile against an inbound invoice."),
_permission(HANDOFF_SCOPE, "Handoff validated XRechnung", "Create a digest-bound handoff only from technically complete valid evidence."),
),
role_templates=(
RoleTemplate(
slug="xrechnung_processor",
name="XRechnung processor",
description="Validate inbound invoices and create governed valid-invoice handoffs.",
permissions=(READ_SCOPE, EXECUTE_SCOPE, HANDOFF_SCOPE),
),
RoleTemplate(
slug="xrechnung_reviewer",
name="XRechnung reviewer",
description="Inspect validation profiles, results, and diagnostics without creating effects.",
permissions=(READ_SCOPE,),
),
),
documentation=(
DocumentationTopic(
id="xrechnung.inbound-validation",
title="Validate and hand off an inbound XRechnung",
summary="Verify safe invoice XML with an exact locally pinned KoSIT engine and rule tree, then create a digest-bound handoff only from complete valid evidence.",
body=(
"An administrator must approve an exact XRechnung version, KoSIT validator release, configuration release, configuration-tree digest, engine digest, scenario file, and minimum validation-step count. A deployment-owned allow-list records approval identity, decision reference, receive-time window, suspension or retirement state, and an optional explicit default; GovOPlaN selects no version itself. The connector verifies those artifacts before each run and never downloads rules while processing an invoice. It rejects dangerous XML before invoking Java. Runner exit, bounded output, technical error markers, VARL identity, matched scenario, configured validation-step count, validity flag, and exactly one accept or reject assessment are checked independently. Technical failure or partial reports yield unknown conformance and can never be handed off. A valid handoff binds tenant, source reference, invoice digest, report digest, and profile digest for an owning Procurement or Payments workflow."
),
layer="configured",
documentation_types=("admin", "user"),
audience=("user", "operator", "module_admin", "auditor"),
related_modules=("files", "procurement", "payments", "records", "audit"),
conditions=(
DocumentationCondition(
any_scopes=(READ_SCOPE, EXECUTE_SCOPE, HANDOFF_SCOPE),
),
),
links=(
DocumentationLink(
label="Inbound validation profile",
href="docs/INBOUND_VALIDATION.md",
kind="repository",
),
),
translations={
"de": {
"title": "Eingehende XRechnung prüfen und übergeben",
"summary": "Sicheres Rechnungs-XML mit exakt lokal festgelegter KoSIT-Engine und Regelstruktur prüfen und nur aus vollständigem gültigem Nachweis eine prüfsummengebundene Übergabe erzeugen.",
"body": "Administrierende müssen eine exakte XRechnungs-Version, KoSIT-Validator-Version, Konfigurationsversion, Prüfsumme der Konfigurationsstruktur, Engine-Prüfsumme, Szenariodatei und Mindestzahl der Prüfschritte freigeben. Eine installationsbezogene Positivliste erfasst Freigabestelle, Entscheidungsreferenz, Eingangszeitfenster, Sperrung oder Ausmusterung und einen optional ausdrücklich gesetzten Standard; GovOPlaN wählt selbst keine Version. Der Konnektor prüft die Artefakte vor jedem Lauf und lädt während der Rechnungsverarbeitung keine Regeln herunter. Gefährliches XML wird vor dem Java-Aufruf abgewiesen. Prozessende, begrenzte Ausgabe, technische Fehlermarker, VARL-Identität, erkanntes Szenario, konfigurierte Prüfschrittzahl, Gültigkeitskennzeichen und genau eine Annahme- oder Ablehnungsempfehlung werden unabhängig geprüft. Technische Fehler oder Teilberichte ergeben unbekannte Konformität und dürfen niemals übergeben werden. Eine gültige Übergabe bindet Mandant, Quellreferenz, Rechnungsprüfsumme, Berichtsprüfsumme und Profilprüfsumme für einen fachlich verantwortlichen Procurement- oder Payments-Ablauf.",
}
},
metadata={
"kind": "workflow",
"prerequisites": [
"An exact KoSIT engine and self-contained XRechnung configuration have been installed locally.",
"The administrator has approved all artifact digests and the expected validation-step count.",
"The profile is approved for the invoice receive time in the deployment allow-list.",
"The owning invoice workflow supplies a stable tenant and source reference.",
],
"steps": [
"Verify the executable, engine digest, complete configuration-tree digest, and scenario location.",
"Select an explicit allow-listed profile or the deployment's explicitly configured default.",
"Reject unsafe or oversized XML, then run the pinned profile with bounded time and output.",
"Check technical completeness separately from the report's semantic validity.",
"Create a handoff only when the report is complete, formally valid, and accepts the invoice.",
],
"limitations": [
"No XRechnung/configuration version is activated by default in this release.",
"The connector creates a handoff contract but does not own payable approval or booking.",
"A syntactically valid report is not trusted when runner output signals a technical failure.",
"Runner stdout and stderr share a fixed 2 MiB capture limit, including one separator byte; the limit is enforced while the validator runs.",
"Reports are limited to 16 MiB and read with at most one extra byte to detect overflow. Pipe capture requires POSIX; deployment resource limits and descendant-process isolation remain separate.",
],
"consequences": [
"Changing any pinned artifact changes the profile digest and requires a new validation.",
"Invalid invoices retain diagnostics but cannot enter the valid-invoice handoff.",
"Technical failure produces unknown conformance, never a semantic rejection or acceptance.",
"Exceeding the output limit stops and reaps the validator and reports technical failure with unknown conformance; operators should check the approved profile using synthetic inputs before retrying.",
],
"verification": "Confirm artifact and profile digests, technical outcome, validation-step count, formal validity, VARL assessment, invoice digest, and report digest before following the handoff reference.",
},
structured_translation_version="1",
structured_translations={
"de": {
"prerequisites": [
"Eine exakte KoSIT-Engine und eine eigenständige XRechnung-Konfiguration sind lokal installiert.",
"Die Administration hat alle Artefaktprüfsummen und die erwartete Zahl der Prüfschritte freigegeben.",
"Das Profil ist in der installationsbezogenen Positivliste für den Eingangszeitpunkt der Rechnung freigegeben.",
"Der fachlich verantwortliche Rechnungsablauf liefert einen stabilen Mandanten- und Quellverweis.",
],
"steps": [
"Ausführbare Datei, Engine-Prüfsumme, vollständige Prüfsumme der Konfigurationsstruktur und Szenariopfad prüfen.",
"Ein ausdrückliches Positivlistenprofil oder den ausdrücklich konfigurierten Installationsstandard auswählen.",
"Unsicheres oder übergroßes XML abweisen und anschließend das festgelegte Profil mit begrenzter Zeit und Ausgabe ausführen.",
"Technische Vollständigkeit getrennt von der semantischen Gültigkeit des Berichts prüfen.",
"Nur bei vollständigem, formal gültigem und die Rechnung annehmendem Bericht eine Übergabe erzeugen.",
],
"limitations": [
"Diese Version aktiviert standardmäßig keine XRechnung- oder Konfigurationsversion.",
"Der Konnektor erzeugt einen Übergabevertrag, führt aber weder Freigabe der Verbindlichkeit noch Buchung.",
"Ein syntaktisch gültiger Bericht ist nicht vertrauenswürdig, wenn die Runner-Ausgabe einen technischen Fehler meldet.",
"Standardausgabe und Standardfehlerausgabe teilen eine feste Erfassungsgrenze von 2 MiB einschließlich eines Trennbytes; die Grenze wird während des Validatorlaufs durchgesetzt.",
"Berichte sind auf 16 MiB begrenzt und werden mit höchstens einem zusätzlichen Byte zur Erkennung einer Überschreitung gelesen. Die Pipe-Erfassung erfordert POSIX; Ressourcengrenzen der Installation und die Isolation nachgelagerter Prozesse bleiben getrennt.",
],
"consequences": [
"Jede Änderung eines festgelegten Artefakts ändert die Profilprüfsumme und erfordert eine neue Validierung.",
"Ungültige Rechnungen bewahren Diagnosen, dürfen aber nicht in die gültige Rechnungsübergabe gelangen.",
"Technischer Fehler ergibt unbekannte Konformität, niemals semantische Ablehnung oder Annahme.",
"Bei Überschreitung der Ausgabegrenze wird der Validator beendet und sein Prozessabschluss abgewartet; das Ergebnis ist ein technischer Fehler mit unbekannter Konformität. Betreibende sollten das freigegebene Profil vor einem erneuten Versuch mit synthetischen Eingaben prüfen.",
],
"verification": "Vor dem Folgen des Übergabeverweises Artefakt- und Profilprüfsummen, technisches Ergebnis, Prüfschrittzahl, formale Gültigkeit, VARL-Bewertung, Rechnungsprüfsumme und Berichtsprüfsumme bestätigen.",
}
},
order=100,
),
DocumentationTopic(
id="xrechnung.reference.validation-profile-and-handoff",
title="XRechnung validation-profile and handoff consequences",
summary=(
"Understand receive-time profile selection, immutable artifact approval, "
"technical uncertainty, and the evidence boundary of a valid-invoice handoff."
),
body=(
"A validation profile names exact engine, configuration, scenario, and artifact "
"digests. Its approval has an actor, decision reference, receive-time window, and "
"approved, suspended, or retired state. Selection uses the invoice receive time and "
"never infers a product version. Unknown, not-yet-valid, expired, suspended, retired, "
"or digest-mismatched profiles fail closed. Validation evidence distinguishes runner "
"completion, report completeness, formal validity, and semantic assessment. Stdout and "
"stderr share a fixed 2 MiB capture limit, including one separator byte, enforced during "
"execution. Overflow stops and reaps the validator; the result is technical failure "
"with unknown conformance and no handoff, even if a report looks valid. Reports are "
"read with a 16 MiB limit plus one probe byte; oversized reports cannot supply trusted "
"validation evidence. A handoff "
"binds the exact invoice, report, and profile digests but does not approve a payable, "
"book an invoice, retain the source file, or replace Records custody."
),
layer="available",
documentation_types=("admin", "user"),
audience=("user", "operator", "module_admin", "auditor"),
related_modules=("files", "procurement", "payments", "records", "audit"),
links=(
DocumentationLink(
label="Inbound validation profile",
href="docs/INBOUND_VALIDATION.md",
kind="repository",
),
),
translations={
"de": {
"title": "Folgen von XRechnung-Prüfprofil und Übergabe",
"summary": (
"Profilauswahl nach Eingangszeit, unveränderliche Artefaktfreigabe, technische Ungewissheit und die Nachweisgrenze einer gültigen Rechnungsübergabe verstehen."
),
"body": (
"Ein Prüfprofil bezeichnet exakte Prüfsummen von Engine, Konfiguration, Szenario und Artefakten. Seine "
"Freigabe enthält Akteur, Entscheidungsverweis, Eingangszeitfenster und den Zustand freigegeben, gesperrt "
"oder ausgemustert. Die Auswahl verwendet den Eingangszeitpunkt der Rechnung und leitet niemals eine "
"Produktversion ab. Unbekannte, noch nicht gültige, abgelaufene, gesperrte, ausgemusterte oder bei der "
"Prüfsumme abweichende Profile werden geschlossen abgewiesen. Der Validierungsnachweis unterscheidet "
"Runner-Abschluss, Berichtsvollständigkeit, formale Gültigkeit und semantische Bewertung. Standardausgabe "
"und Standardfehlerausgabe teilen eine feste Erfassungsgrenze von 2 MiB einschließlich eines Trennbytes, "
"die während der Ausführung durchgesetzt wird. Bei Überschreitung wird der Validator beendet und sein "
"Prozessabschluss abgewartet; das Ergebnis ist ein technischer Fehler mit unbekannter Konformität ohne "
"Übergabe, selbst wenn ein Bericht gültig erscheint. Berichte werden mit einer Grenze von 16 MiB "
"zuzüglich eines Prüfbytes gelesen; übergroße Berichte liefern keinen vertrauenswürdigen "
"Validierungsnachweis. Eine Übergabe "
"bindet exakte Rechnungs-, Berichts- und Profilprüfsummen, genehmigt aber keine Verbindlichkeit, bucht keine "
"Rechnung, bewahrt keine Quelldatei und ersetzt nicht die Verwahrung durch Records."
),
}
},
metadata={
"kind": "reference",
"consequence_classes": {
"approve_profile": "Makes one exact verified artifact set eligible only in its receive-time window.",
"suspend_or_retire_profile": "Blocks selection without rewriting prior validation evidence.",
"fail_closed": "Treats unknown profile or technical outcome as untrusted rather than valid or invalid.",
"exceed_output_limit": "Stops and reaps the validator with a technical-failure reason and blocks handoff regardless of report content.",
"create_handoff": "Binds valid evidence for an owner workflow without approving, booking, or storing the invoice.",
},
},
structured_translation_version="1",
structured_translations={
"de": {
"consequence_classes": {
"approve_profile": "Macht genau einen geprüften Artefaktsatz ausschließlich in seinem Eingangszeitfenster auswählbar.",
"suspend_or_retire_profile": "Sperrt die Auswahl, ohne frühere Validierungsnachweise umzuschreiben.",
"fail_closed": "Behandelt unbekanntes Profil oder technisches Ergebnis als nicht vertrauenswürdig statt als gültig oder ungültig.",
"exceed_output_limit": "Beendet den Validator, wartet seinen Prozessabschluss ab, meldet einen technischen Fehler und sperrt die Übergabe unabhängig vom Berichtsinhalt.",
"create_handoff": "Bindet gültige Nachweise für einen Eigentümerablauf, ohne die Rechnung zu genehmigen, zu buchen oder zu speichern.",
}
}
},
order=110,
),
),
architecture=declared_module_architecture(
layer="data_reporting_integration",
kind="integration",
maturity="vertical_slice",
documentation_ref="docs/INBOUND_VALIDATION.md",
test_ref="tests/test_validation.py",
known_limits=("A deployment-approved XRechnung and KoSIT configuration profile is required; none is activated by default.",),
owned_concepts=("XRechnung validation profile", "validation result", "validated invoice handoff"),
non_owned_concepts=("invoice payable", "procurement approval", "booking status", "invoice file storage"),
recovery_docs=("docs/INBOUND_VALIDATION.md",),
security_docs=("docs/INBOUND_VALIDATION.md",),
operations_docs=("docs/INBOUND_VALIDATION.md",),
),
)
def get_manifest() -> ModuleManifest:
return manifest