Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions packages/verapdf/.github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# These are supported funding model platforms

github: [mooxphp]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
50 changes: 50 additions & 0 deletions packages/verapdf/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Environment
.env
.env.backup

# Composer
/vendor
composer.lock
auth.json

# NPM / Node
/node_modules
npm-debug.log
package-lock.json

# Laravel
/public/hot
/public/storage
/storage/*.key

# PHPUnit
.phpunit.result.cache
phpunit.xml

# Yarn
yarn-error.log

# PHPStan
/build
phpstan.neon

# Testbench
testbench.yaml
/workbench/*

# PHP CS Fixer
.php-cs-fixer.cache

# Homestead
Homestead.json
Homestead.yaml

# IDEs
/.idea
/.vscode

# MacOS
.DS_Store

# Windows
Thumbs.db
7 changes: 7 additions & 0 deletions packages/verapdf/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Changelog

All notable changes to `moox/verapdf` will be documented in this file.

## Unreleased

- Initial package: veraPDF install/validate/doctor, `VeraPdfService`, `VeraPdfResult`, audit model + morph pivot, Pest fixture tests.
21 changes: 21 additions & 0 deletions packages/verapdf/LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) Moox <devs@moox.org>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
184 changes: 184 additions & 0 deletions packages/verapdf/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
![Moox VeraPdf](https://github.com/mooxphp/moox/raw/main/art/banner/record.jpg)

# Moox VeraPdf

veraPDF CLI wrapper for PDF/A-3 validation. The package headless-installs the official greenfield veraPDF distribution, runs the launcher against a PDF, persists audit rows on `verapdf_validations`, and optionally links validations to domain owners via `verapdf_validatables`.

## Features

- veraPDF greenfield install (`verapdf:install`, `--force`) via IzPack headless auto-install (CLI pack only; no GUI)
- Health checks (`verapdf:doctor`) and CLI validation (`verapdf:validate`)
- Programmatic validation via `VeraPdfService` and structured `VeraPdfResult` (MRR XML parsing)
- `RecordVeraPdfValidation` action for audit persistence
- Morph pivot `verapdf_validatables` (UUID owners) for optional multi-owner linking
- Moox-standard layout: config, two stub migrations, `resources/lang` (EN/DE)

## Responsibility Boundaries

- `moox/verapdf` owns veraPDF installation, PDF/A-3 validation, report output paths, and validation audit persistence.
- Owner packages are external; register allowed types under `verapdf.relations.verapdf_validatables.owner_types` when wiring morph history.
- This package stays generic — no e-billing or host-app knowledge.

## Requirements

| Requirement | Purpose |
|-------------|---------|
| `moox/core` | Base model, Moox installer, morph pivot registry |
| Java runtime | Headless IzPack install and veraPDF launcher (`VERAPDF_JAVA_BINARY`, default `java`) |

### Server prerequisites (headless)

Deploy on a server with a **JRE only** — no desktop/X11 packages required.

| Prerequisite | Notes |
|--------------|-------|
| Java **8, 11, 17, or 21** | Prefer a headless JRE on Ubuntu: `sudo apt install default-jre-headless` (or Temurin 17 JRE) |
| No GUI/X11 | `verapdf:install` runs IzPack automated mode with `-Djava.awt.headless=true` and selects the **veraPDF CLI** pack only (GUI/documentation/plugins excluded) |

```bash
# Headless install (CLI pack only)
php artisan verapdf:install

# Confirm Java + CLI launcher + bin/*cli*.jar
php artisan verapdf:doctor

# Smoke-test PDF/A-3 validation
php artisan verapdf:validate /absolute/path/to/file.pdf
```

The auto-install stub matches the pinned greenfield installer **1.30.1+** pack list (`veraPDF CLI` / `veraPDF GUI`). Do not point `VERAPDF_DOWNLOAD_URL` at a pre-1.30 zip without updating `resources/install/auto-install.xml.stub`.

## Installation

```bash
composer require moox/verapdf
php artisan moox:install
```

Install veraPDF artefacts (CLI pack only; safe on headless servers):

```bash
php artisan verapdf:install
```

### Installer integrity

`verapdf:install` verifies the downloaded greenfield zip against a **pinned SHA-256** (`verapdf.installer.sha256` / `VERAPDF_INSTALLER_SHA256`) before extraction, and rejects archive entries that resolve outside the extract directory (zip-slip). A mismatch or unsafe entry aborts with a clear error and does **not** wipe an existing install (even with `--force`).

When bumping the pinned veraPDF version, update `installer.version`, `installer.download_url`, and `installer.sha256` together. How to recompute the digest is documented in [SECURITY.md](SECURITY.md).

Verify Java, CLI launcher, CLI jar, and report directory writability:

```bash
php artisan verapdf:doctor
```

Optionally publish configuration and migrations:

```bash
php artisan vendor:publish --tag=verapdf-config --force
php artisan vendor:publish --tag=verapdf-migrations --force
php artisan config:clear
```

## Usage

### Environment variables

| Variable | Config key | Purpose |
|----------|------------|---------|
| `VERAPDF_BASE_PATH` | `base_path` | Root for veraPDF install (default `storage/app/private/verapdf`) |
| `VERAPDF_VERSION` | `installer.version` | Installer version label |
| `VERAPDF_DOWNLOAD_URL` | `installer.download_url` | Greenfield installer zip URL |
| `VERAPDF_INSTALLER_SHA256` | `installer.sha256` | Pinned SHA-256 of that zip (required) |
| `VERAPDF_JAVA_BINARY` | `java_binary` | Java executable (default `java`) |
| `VERAPDF_FLAVOUR` | `flavour` | PDF/A flavour code (default `3b`) |
| `VERAPDF_OUTPUT_PATH` | `output.path` | Report output directory |

### CLI validation

```bash
php artisan verapdf:validate /absolute/path/to/file.pdf
```

Requires a prior `verapdf:install`. Always persists via `RecordVeraPdfValidation` and prints the validation ID.

### Programmatic validation

```php
use Moox\VeraPdf\Services\VeraPdfService;
use Moox\VeraPdf\Actions\RecordVeraPdfValidation;
use Moox\VeraPdf\Support\VeraPdfOutputPath;

$reportDir = VeraPdfOutputPath::resolve('2026-07-17');
$result = app(VeraPdfService::class)->validate('/path/to/file.pdf', $reportDir);
$validation = app(RecordVeraPdfValidation::class)($result);

if (! app(VeraPdfService::class)->isInstalled()) {
// degrade gracefully — run verapdf:install
}
```

## Database schema

### `verapdf_validations`

| Column | Type | Notes |
|--------|------|-------|
| `id` | bigint PK | |
| `input_path` | string(1024) nullable | Validated PDF path |
| `passed` | boolean | From report `isCompliant` / exit code |
| `errors` | json nullable | Normalized validation messages |
| `report_xml_path` | string(1024) nullable | |
| `report_html_path` | string(1024) nullable | |
| `validated_at` | timestamp | |
| `created_at`, `updated_at` | timestamps | |
| `scope` | string nullable, indexed | Reserved |

### `verapdf_validatables`

| Column | Type | Notes |
|--------|------|-------|
| `id` | bigint PK | |
| `validatable_type`, `validatable_id` | uuid morph | Owner |
| `verapdf_validation_id` | foreignId | Cascade delete |
| `created_at`, `updated_at` | timestamps | |
| `scope` | string nullable, indexed | Reserved |

## Public API

| Kind | FQCN |
|------|------|
| Service | `Moox\VeraPdf\Services\VeraPdfService` |
| Result DTO | `Moox\VeraPdf\DTOs\VeraPdfResult` |
| Record action | `Moox\VeraPdf\Actions\RecordVeraPdfValidation` |
| Output path | `Moox\VeraPdf\Support\VeraPdfOutputPath` |
| Relation config | `Moox\VeraPdf\Support\VeraPdfRelationConfig` |

### Artisan commands

| Command | Options | Description |
|---------|---------|-------------|
| `verapdf:install` | `--force` | Download and headless-install veraPDF (CLI pack only) |
| `verapdf:validate` | `{path}` | Validate PDF, persist audit row |
| `verapdf:doctor` | — | Check Java, CLI launcher/jar, writable output path |

## Running tests

From the package directory:

```bash
composer test
```

Or from the monorepo root:

```bash
php vendor/bin/pest --configuration=packages/verapdf/phpunit.xml packages/verapdf/tests/Unit packages/verapdf/tests/Feature
```

Parsing tests use XML fixtures and do not require a live JVM.

## License

The MIT License (MIT). Please see [our license and copyright information](https://github.com/mooxphp/moox/blob/main/LICENSE.md) for more information.
34 changes: 34 additions & 0 deletions packages/verapdf/SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Security Policy

## Supported Versions

We maintain the current version of `moox/verapdf` actively.

Do not expect security fixes for older versions.

## Reporting a Vulnerability

If you find any security-related bug, please report it to security@moox.org.

Please do not use GitHub issues, to give us enough time to review and fix the issue before others can exploit it.

## Installer supply chain (`verapdf:install`)

The greenfield installer zip is treated as untrusted until verified:

1. **Pinned SHA-256** — `config/verapdf.php` key `installer.sha256` (env: `VERAPDF_INSTALLER_SHA256`) must match the zip bytes at `installer.download_url`. Verification uses `hash_file('sha256')` with `hash_equals`. An empty or malformed pin fails closed. veraPDF also publishes GPG `.asc` signatures on [software.verapdf.org](https://software.verapdf.org/releases/1.30); this package pins the digest in config as the operational integrity check for headless CI/servers without requiring GPG on every host.
2. **Zip-slip rejection** — every ZIP entry is validated before extract; absolute paths and `..` segments are refused (same idea as `VeraPdfOutputPath` subdirectory hardening).
3. **Non-destructive failures** — download and extract run in a system temp staging directory. Checksum or zip-slip failure aborts before IzPack runs and before `--force` deletes an existing `base_path` install.

### Updating the pin when the veraPDF version changes

On a trusted machine:

```bash
URL='https://software.verapdf.org/releases/1.30/verapdf-greenfield-1.30.1-installer.zip'
curl -fsSL -o /tmp/verapdf-installer.zip "$URL"
shasum -a 256 /tmp/verapdf-installer.zip
# or: openssl dgst -sha256 /tmp/verapdf-installer.zip
```

Paste the 64-character hex into `installer.sha256` in the same change that updates `installer.version` and `installer.download_url`. If you override `VERAPDF_DOWNLOAD_URL`, you must also set a matching `VERAPDF_INSTALLER_SHA256`.
Binary file added packages/verapdf/banner.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added packages/verapdf/banner_build.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
65 changes: 65 additions & 0 deletions packages/verapdf/composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
"name": "moox/verapdf",
"description": "veraPDF CLI wrapper for PDF/A-3 validation with audit persistence.",
"keywords": [
"Moox",
"Laravel",
"Filament",
"Moox package",
"Laravel package",
"veraPDF",
"PDF/A"
],
"homepage": "https://moox.org/docs/verapdf",
"license": "MIT",
"authors": [
{
"name": "Moox Developer",
"email": "dev@moox.org",
"role": "Developer"
}
],
"require": {
"moox/core": "dev-main"
},
"autoload": {
"psr-4": {
"Moox\\VeraPdf\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"Moox\\VeraPdf\\Tests\\": "tests/"
}
},
"extra": {
"laravel": {
"providers": [
"Moox\\VeraPdf\\VeraPdfServiceProvider"
]
},
"moox": {
"stability": "dev"
}
},
"minimum-stability": "dev",
"prefer-stable": true,
"require-dev": {
"moox/devtools": "dev-main",
"pestphp/pest": "^4.7",
"orchestra/testbench": "^10.0|^11.0"
},
"scripts": {
"test": [
"@php ../../vendor/bin/pest --configuration=phpunit.xml tests/Unit tests/Feature"
],
"test:arch": [
"@php ../../vendor/bin/pest --configuration=phpunit.xml tests/ArchTest.php"
]
},
"config": {
"allow-plugins": {
"pestphp/pest-plugin": true
}
}
}
Loading
Loading