diff --git a/.github/workflows/release-assets.yml b/.github/workflows/release-assets.yml new file mode 100644 index 0000000..c67e672 --- /dev/null +++ b/.github/workflows/release-assets.yml @@ -0,0 +1,29 @@ +name: release-assets + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + sbom-and-release-assets: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install stable Rust + uses: dtolnay/rust-toolchain@stable + + - name: Generate machine SBOM artifacts + run: bash scripts/generate_release_sbom_v0_2.sh dist/release-v0.2 + + - name: Validate generated JSON artifacts + run: | + python3 -m json.tool dist/release-v0.2/sbom/cyclonedx-rust-v0.2.json >/dev/null + python3 -m json.tool dist/release-v0.2/sbom/spdx-rust-v0.2.json >/dev/null + python3 -m json.tool dist/release-v0.2/RELEASE_ASSETS_MANIFEST-v0.2.json >/dev/null + + - name: Verify release asset checksum/signature script + run: bash scripts/verify_release_assets.sh --self-test diff --git a/CHANGELOG_RU.md b/CHANGELOG_RU.md index 4357f9c..3cc2ab7 100644 --- a/CHANGELOG_RU.md +++ b/CHANGELOG_RU.md @@ -1,5 +1,24 @@ # Журнал изменений +## release-readiness-v0.2 - 2026-06-03 + +Назначение этапа: усилить пакет для коммерческого релиза и реестра российского +ПО за счет машинного SBOM, проверки release assets, пилотного акта приемки и +корректного позиционирования pfSense. + +### Добавлено + +- `scripts/generate_release_sbom_v0_2.sh` - генерация CycloneDX/SPDX SBOM + artifacts в `dist/release-v0.2/`. +- `scripts/verify_release_assets.sh` - проверка `SHA256SUMS*.txt` и detached + signature release assets. +- `.github/workflows/release-assets.yml` - CI self-test checksum/signature + verifier и генерации SBOM. +- `docs/RELEASE_READINESS_V0.2_RU.md` - контрольная карта v0.2. +- `docs/CUSTOMER_PILOT_ACCEPTANCE_RU.md` - шаблон акта приемки пилота. +- `docs/NETWORK_PERIMETER_PFSENSE_RU.md` - pfSense как опциональный + интеграционный слой, не обязательная часть продукта. + ## release-readiness-v0.1 - 2026-06-03 Назначение этапа: довести DetMir/AWatch-rus до проверяемого release-readiness diff --git a/docs/CUSTOMER_PILOT_ACCEPTANCE_RU.md b/docs/CUSTOMER_PILOT_ACCEPTANCE_RU.md new file mode 100644 index 0000000..92ff362 --- /dev/null +++ b/docs/CUSTOMER_PILOT_ACCEPTANCE_RU.md @@ -0,0 +1,92 @@ +# Акт приемки пилота DetMir + +Документ-шаблон для коммерческого пилота DetMir/AWatch-rus. Перед передачей +заказчику заполнить реквизиты сторон, даты, состав стенда и результаты +проверок. Публичная версия не должна содержать live IP, домены, ФИО +сотрудников, case IDs, evidence paths или screenshots с реальными данными. + +## 1. Стороны + +| Поле | Значение | +|---|---| +| Заказчик | `` | +| Исполнитель/правообладатель | `` | +| Проект | DetMir, программный комплекс AWatch-rus | +| Период пилота | `` - `` | +| Контур | `` | + +## 2. Цель пилота + +Проверить применимость DetMir для: + +- мониторинга активности рабочих мест; +- управленческой аналитики Workforce; +- технического аудита ИТ-контура; +- фиксации DLP-lite/ИБ-событий; +- просмотра evidence и отчетов в портале; +- контроля готовности системы через signed readiness bundle. + +## 3. Состав поставки + +| Компонент | Проверка | +|---|---| +| DetMir portal | вход, роли оператора/руководителя/владельца | +| ActivityWatch telemetry | актуальность bucket/event данных | +| Workforce analytics | индекс активности, веса приложений, drill-down | +| DLP-lite incidents | USB/print/clipboard/file/email/browser signals, если включены | +| Evidence workflow | preview/download/view audit | +| Grafana dashboards | наличие данных и отсутствие query errors | +| Readiness bundle | checksum/signature/fingerprint | +| Prometheus alerts | readiness/signature alerts настроены | + +## 4. Критерии приемки + +Пилот считается успешным, если: + +- портал доступен ответственным пользователям заказчика; +- telemetry freshness находится в согласованных пределах; +- readiness status = `OK` или все `WARN` имеют согласованный план устранения; +- signed readiness bundle проходит проверку; +- не менее одного управленческого отчета сформировано и принято заказчиком; +- не менее одного test incident/evidence workflow пройден end-to-end; +- заказчик подтвердил, что состав данных и уведомлений соответствует правилам + внутреннего контроля и локальным нормативным документам. + +## 5. Результаты проверок + +| Проверка | Результат | Комментарий | +|---|---|---| +| Portal login | `` | `` | +| Readiness bundle | `` | `` | +| Workforce report | `` | `` | +| DLP-lite incident | `` | `` | +| Evidence preview/download | `` | `` | +| Grafana dashboards | `` | `` | +| Alerting | `` | `` | + +## 6. Ограничения пилота + +- DetMir не заявляется как сертифицированная СЗИ, SIEM, EDR/XDR или + enterprise DLP. +- pfSense/network quarantine интеграции являются опциональным интеграционным + слоем и не входят в обязательный состав пилота. +- Telegram runtime может использоваться как интеграционный канал уведомлений, + но не является ядром продукта. +- Результаты Workforce analytics являются управленческими proxy-метриками и + должны трактоваться с учетом ролей, весов приложений и локальных регламентов. + +## 7. Решение + +| Решение | Отметка | +|---|---| +| Пилот принят без замечаний | `` | +| Пилот принят с замечаниями | `` | +| Требуется доработка | `` | +| Рекомендуется коммерческое внедрение | `` | + +## 8. Подписи + +| Сторона | ФИО/должность | Подпись | Дата | +|---|---|---|---| +| Заказчик | `` | | | +| Исполнитель | `` | | | diff --git a/docs/NETWORK_PERIMETER_PFSENSE_RU.md b/docs/NETWORK_PERIMETER_PFSENSE_RU.md new file mode 100644 index 0000000..6462b15 --- /dev/null +++ b/docs/NETWORK_PERIMETER_PFSENSE_RU.md @@ -0,0 +1,80 @@ +# Network perimeter и pfSense + +Документ описывает роль pfSense в архитектуре DetMir/AWatch-rus для +коммерческих внедрений и экспертной оценки. pfSense рассматривается как +опциональный интеграционный слой сетевого периметра, а не как обязательная +часть продукта. + +## 1. Позиция продукта + +DetMir/AWatch-rus поставляет: + +- сбор и нормализацию endpoint/server telemetry; +- Workforce analytics; +- технический аудит; +- DLP-lite/ИБ evidence workflow; +- readiness checks и portal reporting. + +Сетевой шлюз, firewall, NAT, VPN и quarantine enforcement могут быть +интегрированы с DetMir, но не входят в минимальный состав продукта. + +## 2. Роль pfSense + +pfSense может использоваться как: + +- источник сетевого контекста; +- внешний policy enforcement point; +- шлюз для ограничений VLAN/alias/rules; +- источник логов для корреляции с endpoint activity. + +DetMir не требует pfSense для базовой работы портала, readiness, workforce, +DLP-lite evidence и отчетов. + +## 3. Интеграционные границы + +| Слой | Статус | +|---|---| +| Endpoint telemetry | обязательный слой DetMir | +| Server-side checks/readiness | обязательный слой DetMir | +| Portal/Grafana/Prometheus | обязательный слой DetMir | +| pfSense logs/context | опциональная интеграция | +| pfSense policy enforcement | опциональная интеграция с отдельным решением | +| Автоматический quarantine | не включать без отдельного согласования | + +## 4. Безопасный режим внедрения + +По умолчанию: + +- DetMir только читает сетевой контекст, если интеграция включена; +- любые изменения firewall/NAT/VPN/quarantine запрещены без отдельного change + request; +- pfSense credentials хранятся вне Git и вне public release assets; +- public docs используют placeholders и TEST-NET адреса. + +## 5. Что не заявлять + +Для реестра российского ПО и публичной экспертизы не позиционировать DetMir как: + +- firewall; +- NAC; +- VPN gateway; +- сертифицированное средство сетевой защиты; +- обязательный модуль управления pfSense. + +Корректная формулировка: + +> DetMir поддерживает интеграцию с сетевым периметром заказчика, включая +> pfSense-compatible gateways, как внешний источник контекста и опциональную +> точку применения политик. + +## 6. Будущий roadmap + +Возможные этапы развития: + +1. Read-only import сетевых событий. +2. Корреляция endpoint activity и VPN/network context. +3. Manual approval workflow для сетевых ограничений. +4. Controlled enforcement через allowlist политик. +5. Audit trail каждого сетевого действия. + +До отдельного решения владельца продукта pfSense runtime остается no-touch. diff --git a/docs/RELEASE_MANIFEST_2026-06.md b/docs/RELEASE_MANIFEST_2026-06.md index c9ae570..1b90000 100644 --- a/docs/RELEASE_MANIFEST_2026-06.md +++ b/docs/RELEASE_MANIFEST_2026-06.md @@ -30,6 +30,28 @@ Additional readiness artifacts: - `docs/screenshots/release-v0.1/portal-reports-risk.png` - `docs/screenshots/release-v0.1/portal-owner-summary.png` +## Release-readiness v0.2 overlay + +Additional release/commercial-readiness artifacts: + +- `scripts/generate_release_sbom_v0_2.sh` +- `scripts/verify_release_assets.sh` +- `.github/workflows/release-assets.yml` +- `docs/RELEASE_READINESS_V0.2_RU.md` +- `docs/CUSTOMER_PILOT_ACCEPTANCE_RU.md` +- `docs/NETWORK_PERIMETER_PFSENSE_RU.md` + +Generated GitHub Release assets: + +- `sbom/cyclonedx-rust-v0.2.json` +- `sbom/spdx-rust-v0.2.json` +- `sbom/cargo-metadata-v0.2.json` +- `sbom/cargo-tree-v0.2.txt` +- `sbom/python-inputs-v0.2.txt` +- `RELEASE_ASSETS_MANIFEST-v0.2.json` +- `SHA256SUMS-v0.2.txt` +- `SHA256SUMS-v0.2.txt.sig` + ## Artifacts ```text diff --git a/docs/RELEASE_NOTES_2026-06.md b/docs/RELEASE_NOTES_2026-06.md index ed3cbbb..bae3291 100644 --- a/docs/RELEASE_NOTES_2026-06.md +++ b/docs/RELEASE_NOTES_2026-06.md @@ -16,6 +16,16 @@ Release type: public expert-review/source package. - architecture схема `docs/diagrams/release-readiness-v0.1.md`; - обезличенные screenshots портала в `docs/screenshots/release-v0.1/`. +## Release-readiness v0.2 addendum + +Добавлен слой коммерческого/release hardening: + +- machine SBOM generation для CycloneDX и SPDX; +- CI self-test checksum/signature verifier; +- шаблон customer pilot acceptance act; +- документ network perimeter/pfSense как optional integration layer; +- требование подписанного Git tag и detached signature для release assets. + ## Что входит - Обезличенный public source package DetMir/AWatch-rus. diff --git a/docs/RELEASE_READINESS_V0.2_RU.md b/docs/RELEASE_READINESS_V0.2_RU.md new file mode 100644 index 0000000..f350655 --- /dev/null +++ b/docs/RELEASE_READINESS_V0.2_RU.md @@ -0,0 +1,117 @@ +# Release readiness v0.2 + +`release-readiness-v0.2` усиливает v0.1 в части коммерческого релиза и +подготовки к реестру российского ПО. + +Дата фиксации: `2026-06-03`. + +## 1. Что добавлено + +| Блок | Файл | +|---|---| +| Machine SBOM generation | `scripts/generate_release_sbom_v0_2.sh` | +| Release asset verification | `scripts/verify_release_assets.sh` | +| CI checksum/signature self-test | `.github/workflows/release-assets.yml` | +| Pilot acceptance act | `docs/CUSTOMER_PILOT_ACCEPTANCE_RU.md` | +| pfSense perimeter positioning | `docs/NETWORK_PERIMETER_PFSENSE_RU.md` | + +## 2. Machine SBOM as GitHub Release asset + +Сгенерировать assets: + +```bash +bash scripts/generate_release_sbom_v0_2.sh dist/release-v0.2 +``` + +Ожидаемые файлы: + +```text +dist/release-v0.2/sbom/cyclonedx-rust-v0.2.json +dist/release-v0.2/sbom/spdx-rust-v0.2.json +dist/release-v0.2/sbom/cargo-metadata-v0.2.json +dist/release-v0.2/sbom/cargo-tree-v0.2.txt +dist/release-v0.2/sbom/python-inputs-v0.2.txt +dist/release-v0.2/RELEASE_ASSETS_MANIFEST-v0.2.json +dist/release-v0.2/SHA256SUMS-v0.2.txt +``` + +Эти файлы публикуются как GitHub Release assets. Они не хранятся в tracked +source, потому что `dist/` является generated output. + +## 3. Подпись release assets + +Release manager подписывает checksum file detached signature: + +```bash +openssl dgst -sha256 -sign \ + -out dist/release-v0.2/SHA256SUMS-v0.2.txt.sig \ + dist/release-v0.2/SHA256SUMS-v0.2.txt +``` + +Проверка: + +```bash +RELEASE_VERIFY_PUBLIC_KEY= \ + bash scripts/verify_release_assets.sh dist/release-v0.2 +``` + +## 4. Подписанный Git tag + +Требование для финального релиза: + +```bash +git tag -s release-readiness-v0.2 -m "release-readiness-v0.2" +git tag -v release-readiness-v0.2 +git push origin release-readiness-v0.2 +``` + +Tag должен быть подписан пользовательским ключом правообладателя/maintainer, а +не сторонним системным ключом пакетов ОС. + +## 5. CI gate + +Workflow: + +```text +.github/workflows/release-assets.yml +``` + +Проверяет: + +- генерацию CycloneDX/SPDX JSON; +- валидность JSON; +- self-test checksum/signature verifier; +- отрицательные сценарии checksum mismatch и missing signature. + +## 6. Pilot acceptance + +Шаблон: + +```text +docs/CUSTOMER_PILOT_ACCEPTANCE_RU.md +``` + +Использовать для коммерческого пилота после установки и первичной настройки. +Публичная версия должна оставаться обезличенной. + +## 7. pfSense perimeter + +Документ: + +```text +docs/NETWORK_PERIMETER_PFSENSE_RU.md +``` + +Ключевая позиция: pfSense является опциональным интеграционным слоем, а не +обязательной частью продукта DetMir/AWatch-rus. + +## 8. Acceptance checklist + +- SBOM assets сгенерированы. +- `SHA256SUMS-v0.2.txt` создан. +- `SHA256SUMS-v0.2.txt.sig` создан release private key. +- `scripts/verify_release_assets.sh` проходит с public key. +- Git tag подписан ключом maintainer. +- GitHub release содержит SBOM JSON/TXT, manifest, checksum и signature. +- `CUSTOMER_PILOT_ACCEPTANCE_RU.md` заполнен для пилотного заказчика. +- pfSense не описан как обязательный компонент продукта. diff --git a/docs/SBOM_RELEASE_CHECKLIST_RU.md b/docs/SBOM_RELEASE_CHECKLIST_RU.md index ade21eb..3316881 100644 --- a/docs/SBOM_RELEASE_CHECKLIST_RU.md +++ b/docs/SBOM_RELEASE_CHECKLIST_RU.md @@ -147,6 +147,22 @@ git ls-files '*.py' Сформировать машинные перечни зависимостей. +Для release-readiness v0.2 используется единый генератор: + +```bash +bash scripts/generate_release_sbom_v0_2.sh dist/release-v0.2 +``` + +Он формирует: + +- `cyclonedx-rust-v0.2.json`; +- `spdx-rust-v0.2.json`; +- `cargo-metadata-v0.2.json`; +- `cargo-tree-v0.2.txt`; +- `python-inputs-v0.2.txt`; +- `RELEASE_ASSETS_MANIFEST-v0.2.json`; +- `SHA256SUMS-v0.2.txt`. + Rust: ```bash @@ -202,6 +218,19 @@ sha256sum dist/* install-kit-awindows-*.zip install-kit-awindows-*.tar.gz \ > SHA256SUMS ``` +Проверка release assets v0.2: + +```bash +RELEASE_VERIFY_PUBLIC_KEY= \ + bash scripts/verify_release_assets.sh dist/release-v0.2 +``` + +CI self-test: + +```bash +bash scripts/verify_release_assets.sh --self-test +``` + ## 9. Документы для реестра российского ПО Проверить наличие и актуальность: diff --git a/scripts/generate_release_sbom_v0_2.sh b/scripts/generate_release_sbom_v0_2.sh new file mode 100755 index 0000000..9604bd4 --- /dev/null +++ b/scripts/generate_release_sbom_v0_2.sh @@ -0,0 +1,128 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +OUT_DIR="${1:-"$ROOT_DIR/dist/release-v0.2"}" +SBOM_DIR="$OUT_DIR/sbom" +VERSION="release-readiness-v0.2" + +mkdir -p "$SBOM_DIR" + +cd "$ROOT_DIR" + +COMMIT="$(git rev-parse HEAD)" +GENERATED_AT="$(date -u '+%Y-%m-%dT%H:%M:%SZ')" + +cargo metadata --manifest-path adk-rust/Cargo.toml --format-version 1 \ + > "$SBOM_DIR/cargo-metadata-v0.2.json" + +(cd adk-rust && cargo tree --workspace) \ + > "$SBOM_DIR/cargo-tree-v0.2.txt" + +find aw-server clickhouse-1c detmir-mcp scripts -maxdepth 4 -type f \ + \( -name 'requirements.txt' -o -name 'pyproject.toml' -o -name 'setup.py' \) \ + -print -exec sha256sum {} \; \ + > "$SBOM_DIR/python-inputs-v0.2.txt" + +python3 - "$SBOM_DIR/cargo-metadata-v0.2.json" "$SBOM_DIR/cyclonedx-rust-v0.2.json" "$SBOM_DIR/spdx-rust-v0.2.json" "$COMMIT" "$GENERATED_AT" <<'PY' +import json +import sys +from pathlib import Path + +metadata_path, cyclonedx_path, spdx_path, commit, generated_at = sys.argv[1:] +metadata = json.loads(Path(metadata_path).read_text(encoding="utf-8")) +packages = sorted(metadata.get("packages", []), key=lambda item: (item.get("name", ""), item.get("version", ""))) + +components = [] +spdx_packages = [] +for package in packages: + name = package.get("name") or "unknown" + version = package.get("version") or "0" + license_value = package.get("license") or "NOASSERTION" + components.append({ + "type": "library", + "bom-ref": f"pkg:cargo/{name}@{version}", + "name": name, + "version": version, + "licenses": [{"expression": license_value}] if license_value != "NOASSERTION" else [], + "purl": f"pkg:cargo/{name}@{version}", + }) + spdx_packages.append({ + "name": name, + "SPDXID": f"SPDXRef-Package-{name.replace('_', '-').replace('.', '-')}-{version.replace('.', '-')}", + "versionInfo": version, + "downloadLocation": "NOASSERTION", + "filesAnalyzed": False, + "licenseConcluded": "NOASSERTION", + "licenseDeclared": license_value, + "copyrightText": "NOASSERTION", + }) + +cyclonedx = { + "bomFormat": "CycloneDX", + "specVersion": "1.5", + "serialNumber": f"urn:uuid:00000000-0000-4000-8000-{commit[:12].ljust(12, '0')}", + "version": 1, + "metadata": { + "timestamp": generated_at, + "component": { + "type": "application", + "name": "DetMir AWatch-rus", + "version": "release-readiness-v0.2", + "bom-ref": "pkg:generic/detmir-awatch-rus@release-readiness-v0.2", + }, + "properties": [ + {"name": "git.commit", "value": commit}, + {"name": "scope", "value": "Rust workspace dependencies"}, + ], + }, + "components": components, +} + +spdx = { + "spdxVersion": "SPDX-2.3", + "dataLicense": "CC0-1.0", + "SPDXID": "SPDXRef-DOCUMENT", + "name": "DetMir AWatch-rus release-readiness-v0.2 Rust SBOM", + "documentNamespace": f"https://github.com/igor04091968/AWatch-rus/sbom/{commit}", + "creationInfo": { + "created": generated_at, + "creators": ["Tool: scripts/generate_release_sbom_v0_2.sh"], + }, + "packages": spdx_packages, +} + +Path(cyclonedx_path).write_text(json.dumps(cyclonedx, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") +Path(spdx_path).write_text(json.dumps(spdx, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") +PY + +cat > "$OUT_DIR/RELEASE_ASSETS_MANIFEST-v0.2.json" < SHA256SUMS-v0.2.txt +) + +cat < + scripts/verify_release_assets.sh --self-test + +Checks: + - SHA256SUMS*.txt exists and verifies all release assets. + - Each SHA256SUMS*.txt has a detached signature (*.sig). + - Signature is verified when RELEASE_VERIFY_PUBLIC_KEY is set. + - Without RELEASE_VERIFY_PUBLIC_KEY, signature presence is still enforced. +EOF +} + +fail() { + echo "verify_release_assets: FAIL: $*" >&2 + exit 1 +} + +verify_dir() { + local asset_dir="$1" + [[ -d "$asset_dir" ]] || fail "asset directory does not exist: $asset_dir" + + local sums + sums="$(find "$asset_dir" -maxdepth 1 -type f -name 'SHA256SUMS*.txt' | sort | head -n 1)" + [[ -n "$sums" ]] || fail "SHA256SUMS*.txt not found in $asset_dir" + [[ -s "$sums" ]] || fail "checksum file is empty: $sums" + [[ -f "$sums.sig" ]] || fail "detached signature is missing: $sums.sig" + + (cd "$asset_dir" && sha256sum -c "$(basename "$sums")") || fail "checksum verification failed: $sums" + + if [[ -n "${RELEASE_VERIFY_PUBLIC_KEY:-}" ]]; then + [[ -f "$RELEASE_VERIFY_PUBLIC_KEY" ]] || fail "public key not found: $RELEASE_VERIFY_PUBLIC_KEY" + openssl dgst -sha256 -verify "$RELEASE_VERIFY_PUBLIC_KEY" -signature "$sums.sig" "$sums" >/dev/null \ + || fail "signature verification failed: $sums.sig" + echo "verify_release_assets: signature verified with $RELEASE_VERIFY_PUBLIC_KEY" + else + echo "verify_release_assets: signature file present; cryptographic verification skipped because RELEASE_VERIFY_PUBLIC_KEY is not set" + fi + + echo "verify_release_assets: OK ($asset_dir)" +} + +self_test() { + local tmp + tmp="$(mktemp -d)" + trap 'rm -rf "$tmp"' RETURN + + printf 'release asset\n' > "$tmp/asset.txt" + (cd "$tmp" && sha256sum asset.txt > SHA256SUMS-v0.2.txt) + + openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out "$tmp/private-key.pem" >/dev/null 2>&1 + openssl pkey -in "$tmp/private-key.pem" -pubout -out "$tmp/public-key.pem" >/dev/null 2>&1 + openssl dgst -sha256 -sign "$tmp/private-key.pem" -out "$tmp/SHA256SUMS-v0.2.txt.sig" "$tmp/SHA256SUMS-v0.2.txt" + + RELEASE_VERIFY_PUBLIC_KEY="$tmp/public-key.pem" verify_dir "$tmp" + + cp "$tmp/SHA256SUMS-v0.2.txt" "$tmp/SHA256SUMS-v0.2.txt.good" + printf 'tampered\n' > "$tmp/asset.txt" + if (RELEASE_VERIFY_PUBLIC_KEY="$tmp/public-key.pem" verify_dir "$tmp") >/dev/null 2>&1; then + fail "self-test did not catch checksum mismatch" + fi + mv "$tmp/SHA256SUMS-v0.2.txt.good" "$tmp/SHA256SUMS-v0.2.txt" + printf 'release asset\n' > "$tmp/asset.txt" + rm -f "$tmp/SHA256SUMS-v0.2.txt.sig" + if (RELEASE_VERIFY_PUBLIC_KEY="$tmp/public-key.pem" verify_dir "$tmp") >/dev/null 2>&1; then + fail "self-test did not catch missing signature" + fi + + echo "verify_release_assets: self-test OK" +} + +if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then + usage + exit 0 +fi + +if [[ "${1:-}" == "--self-test" ]]; then + command -v openssl >/dev/null 2>&1 || fail "openssl is required for --self-test" + self_test + exit 0 +fi + +[[ $# -eq 1 ]] || { + usage >&2 + exit 2 +} + +verify_dir "$1"