Files
git.stella-ops.org/.gitea/workflows/schema-validation.yml
StellaOps Bot 2a06f780cf sprints work
2025-12-25 12:19:12 +02:00

323 lines
11 KiB
YAML

# Schema Validation CI Workflow
# Sprint: SPRINT_8200_0001_0003_sbom_schema_validation_ci
# Tasks: SCHEMA-8200-007 through SCHEMA-8200-011
#
# Purpose: Validate SBOM fixtures against official JSON schemas to detect
# schema drift before runtime. Fails CI if any fixture is invalid.
name: Schema Validation
on:
pull_request:
paths:
- 'bench/golden-corpus/**'
- 'src/Scanner/**'
- 'docs/schemas/**'
- 'scripts/validate-*.sh'
- '.gitea/workflows/schema-validation.yml'
push:
branches: [main]
paths:
- 'bench/golden-corpus/**'
- 'src/Scanner/**'
- 'docs/schemas/**'
- 'scripts/validate-*.sh'
env:
SBOM_UTILITY_VERSION: "0.16.0"
jobs:
validate-cyclonedx:
name: Validate CycloneDX Fixtures
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install sbom-utility
run: |
curl -sSfL "https://github.com/CycloneDX/sbom-utility/releases/download/v${SBOM_UTILITY_VERSION}/sbom-utility-v${SBOM_UTILITY_VERSION}-linux-amd64.tar.gz" | tar xz
sudo mv sbom-utility /usr/local/bin/
sbom-utility --version
- name: Validate CycloneDX fixtures
run: |
set -e
SCHEMA="docs/schemas/cyclonedx-bom-1.6.schema.json"
FIXTURE_DIRS=(
"bench/golden-corpus"
"tests/fixtures"
"seed-data"
)
FOUND=0
PASSED=0
FAILED=0
for dir in "${FIXTURE_DIRS[@]}"; do
if [ -d "$dir" ]; then
while IFS= read -r -d '' file; do
if grep -q '"bomFormat".*"CycloneDX"' "$file" 2>/dev/null; then
FOUND=$((FOUND + 1))
echo "::group::Validating: $file"
if sbom-utility validate --input-file "$file" --schema "$SCHEMA" 2>&1; then
echo "✅ PASS: $file"
PASSED=$((PASSED + 1))
else
echo "❌ FAIL: $file"
FAILED=$((FAILED + 1))
fi
echo "::endgroup::"
fi
done < <(find "$dir" -name '*.json' -type f -print0 2>/dev/null || true)
fi
done
echo "================================================"
echo "CycloneDX Validation Summary"
echo "================================================"
echo "Found: $FOUND fixtures"
echo "Passed: $PASSED"
echo "Failed: $FAILED"
echo "================================================"
if [ "$FAILED" -gt 0 ]; then
echo "::error::$FAILED CycloneDX fixtures failed validation"
exit 1
fi
if [ "$FOUND" -eq 0 ]; then
echo "::warning::No CycloneDX fixtures found to validate"
fi
validate-spdx:
name: Validate SPDX Fixtures
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install SPDX tools
run: |
pip install spdx-tools
pip install check-jsonschema
- name: Validate SPDX fixtures
run: |
set -e
SCHEMA="docs/schemas/spdx-jsonld-3.0.1.schema.json"
FIXTURE_DIRS=(
"bench/golden-corpus"
"tests/fixtures"
"seed-data"
)
FOUND=0
PASSED=0
FAILED=0
for dir in "${FIXTURE_DIRS[@]}"; do
if [ -d "$dir" ]; then
while IFS= read -r -d '' file; do
# Check for SPDX markers
if grep -qE '"spdxVersion"|"@context".*spdx' "$file" 2>/dev/null; then
FOUND=$((FOUND + 1))
echo "::group::Validating: $file"
# Try pyspdxtools first (semantic validation)
if pyspdxtools validate "$file" 2>&1; then
echo "✅ PASS (semantic): $file"
PASSED=$((PASSED + 1))
# Fall back to JSON schema validation
elif check-jsonschema --schemafile "$SCHEMA" "$file" 2>&1; then
echo "✅ PASS (schema): $file"
PASSED=$((PASSED + 1))
else
echo "❌ FAIL: $file"
FAILED=$((FAILED + 1))
fi
echo "::endgroup::"
fi
done < <(find "$dir" -name '*.json' -type f -print0 2>/dev/null || true)
fi
done
echo "================================================"
echo "SPDX Validation Summary"
echo "================================================"
echo "Found: $FOUND fixtures"
echo "Passed: $PASSED"
echo "Failed: $FAILED"
echo "================================================"
if [ "$FAILED" -gt 0 ]; then
echo "::error::$FAILED SPDX fixtures failed validation"
exit 1
fi
if [ "$FOUND" -eq 0 ]; then
echo "::warning::No SPDX fixtures found to validate"
fi
validate-vex:
name: Validate OpenVEX Fixtures
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install ajv-cli
run: npm install -g ajv-cli ajv-formats
- name: Validate OpenVEX fixtures
run: |
set -e
SCHEMA="docs/schemas/openvex-0.2.0.schema.json"
FIXTURE_DIRS=(
"bench/golden-corpus"
"bench/vex-lattice"
"tests/fixtures"
"seed-data"
)
FOUND=0
PASSED=0
FAILED=0
for dir in "${FIXTURE_DIRS[@]}"; do
if [ -d "$dir" ]; then
while IFS= read -r -d '' file; do
# Check for OpenVEX markers
if grep -qE '"@context".*openvex|"@type".*"https://openvex' "$file" 2>/dev/null; then
FOUND=$((FOUND + 1))
echo "::group::Validating: $file"
if ajv validate -s "$SCHEMA" -d "$file" --strict=false -c ajv-formats 2>&1; then
echo "✅ PASS: $file"
PASSED=$((PASSED + 1))
else
echo "❌ FAIL: $file"
FAILED=$((FAILED + 1))
fi
echo "::endgroup::"
fi
done < <(find "$dir" -name '*.json' -type f -print0 2>/dev/null || true)
fi
done
echo "================================================"
echo "OpenVEX Validation Summary"
echo "================================================"
echo "Found: $FOUND fixtures"
echo "Passed: $PASSED"
echo "Failed: $FAILED"
echo "================================================"
if [ "$FAILED" -gt 0 ]; then
echo "::error::$FAILED OpenVEX fixtures failed validation"
exit 1
fi
if [ "$FOUND" -eq 0 ]; then
echo "::warning::No OpenVEX fixtures found to validate"
fi
# Negative testing: verify that invalid fixtures are correctly rejected
validate-negative:
name: Validate Negative Test Cases
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install sbom-utility
run: |
curl -sSfL "https://github.com/CycloneDX/sbom-utility/releases/download/v${SBOM_UTILITY_VERSION}/sbom-utility-v${SBOM_UTILITY_VERSION}-linux-amd64.tar.gz" | tar xz
sudo mv sbom-utility /usr/local/bin/
sbom-utility --version
- name: Verify invalid fixtures fail validation
run: |
set -e
SCHEMA="docs/schemas/cyclonedx-bom-1.6.schema.json"
INVALID_DIR="tests/fixtures/invalid"
if [ ! -d "$INVALID_DIR" ]; then
echo "::warning::No invalid fixtures directory found at $INVALID_DIR"
exit 0
fi
EXPECTED_FAILURES=0
ACTUAL_FAILURES=0
UNEXPECTED_PASSES=0
while IFS= read -r -d '' file; do
if grep -q '"bomFormat".*"CycloneDX"' "$file" 2>/dev/null; then
EXPECTED_FAILURES=$((EXPECTED_FAILURES + 1))
echo "::group::Testing invalid fixture: $file"
# This SHOULD fail - if it passes, that's an error
if sbom-utility validate --input-file "$file" --schema "$SCHEMA" 2>&1; then
echo "❌ UNEXPECTED PASS: $file (should have failed validation)"
UNEXPECTED_PASSES=$((UNEXPECTED_PASSES + 1))
else
echo "✅ EXPECTED FAILURE: $file (correctly rejected)"
ACTUAL_FAILURES=$((ACTUAL_FAILURES + 1))
fi
echo "::endgroup::"
fi
done < <(find "$INVALID_DIR" -name '*.json' -type f -print0 2>/dev/null || true)
echo "================================================"
echo "Negative Test Summary"
echo "================================================"
echo "Expected failures: $EXPECTED_FAILURES"
echo "Actual failures: $ACTUAL_FAILURES"
echo "Unexpected passes: $UNEXPECTED_PASSES"
echo "================================================"
if [ "$UNEXPECTED_PASSES" -gt 0 ]; then
echo "::error::$UNEXPECTED_PASSES invalid fixtures passed validation unexpectedly"
exit 1
fi
if [ "$EXPECTED_FAILURES" -eq 0 ]; then
echo "::warning::No invalid CycloneDX fixtures found for negative testing"
fi
echo "✅ All invalid fixtures correctly rejected by schema validation"
summary:
name: Validation Summary
runs-on: ubuntu-latest
needs: [validate-cyclonedx, validate-spdx, validate-vex, validate-negative]
if: always()
steps:
- name: Check results
run: |
echo "Schema Validation Results"
echo "========================="
echo "CycloneDX: ${{ needs.validate-cyclonedx.result }}"
echo "SPDX: ${{ needs.validate-spdx.result }}"
echo "OpenVEX: ${{ needs.validate-vex.result }}"
echo "Negative Tests: ${{ needs.validate-negative.result }}"
if [ "${{ needs.validate-cyclonedx.result }}" = "failure" ] || \
[ "${{ needs.validate-spdx.result }}" = "failure" ] || \
[ "${{ needs.validate-vex.result }}" = "failure" ] || \
[ "${{ needs.validate-negative.result }}" = "failure" ]; then
echo "::error::One or more schema validations failed"
exit 1
fi
echo "✅ All schema validations passed or skipped"