Files
git.stella-ops.org/.github/workflows/examples/example-verdict-sign.yml
StellaOps Bot 907783f625 Add property-based tests for SBOM/VEX document ordering and Unicode normalization determinism
- Implement `SbomVexOrderingDeterminismProperties` for testing component list and vulnerability metadata hash consistency.
- Create `UnicodeNormalizationDeterminismProperties` to validate NFC normalization and Unicode string handling.
- Add project file for `StellaOps.Testing.Determinism.Properties` with necessary dependencies.
- Introduce CI/CD template validation tests including YAML syntax checks and documentation content verification.
- Create validation script for CI/CD templates ensuring all required files and structures are present.
2025-12-26 15:17:58 +02:00

192 lines
5.6 KiB
YAML

# .github/workflows/examples/example-verdict-sign.yml
# Example: Sign policy verdict with keyless signing
#
# This example shows how to:
# 1. Run StellaOps policy evaluation
# 2. Sign the verdict with keyless signing
# 3. Use verdict in deployment gate
#
# Policy verdicts provide:
# - Cryptographic proof of policy evaluation result
# - Binding to specific image and policy version
# - Evidence for audit and compliance
name: Policy Verdict Gate
on:
push:
branches: [main]
workflow_dispatch:
inputs:
image:
description: 'Container image to evaluate (with digest)'
required: true
type: string
policy:
description: 'Policy pack ID'
required: false
default: 'default'
type: string
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
evaluate:
runs-on: ubuntu-latest
permissions:
contents: read
packages: read
outputs:
verdict: ${{ steps.eval.outputs.verdict }}
verdict-digest: ${{ steps.eval.outputs.verdict-digest }}
image-digest: ${{ steps.resolve.outputs.digest }}
passed: ${{ steps.eval.outputs.passed }}
steps:
- name: Install StellaOps CLI
uses: stella-ops/setup-cli@v1
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Resolve Image
id: resolve
run: |
if [[ -n "${{ github.event.inputs.image }}" ]]; then
IMAGE="${{ github.event.inputs.image }}"
else
IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}"
fi
# Resolve to digest
if [[ ! "$IMAGE" =~ @sha256: ]]; then
DIGEST=$(docker manifest inspect "$IMAGE" -v | jq -r '.Descriptor.digest')
IMAGE="${IMAGE%%:*}@${DIGEST}"
else
DIGEST="${IMAGE##*@}"
fi
echo "image=${IMAGE}" >> $GITHUB_OUTPUT
echo "digest=${DIGEST}" >> $GITHUB_OUTPUT
- name: Run Policy Evaluation
id: eval
env:
STELLAOPS_URL: 'https://api.stella-ops.org'
run: |
set -euo pipefail
IMAGE="${{ steps.resolve.outputs.image }}"
POLICY="${{ github.event.inputs.policy || 'default' }}"
echo "::group::Evaluating policy '${POLICY}' against ${IMAGE}"
RESULT=$(stella policy evaluate \
--image "${IMAGE}" \
--policy "${POLICY}" \
--output json)
echo "$RESULT" | jq .
echo "::endgroup::"
# Extract verdict
VERDICT=$(echo "$RESULT" | jq -r '.verdict')
VERDICT_DIGEST=$(echo "$RESULT" | jq -r '.verdictDigest')
PASSED=$(echo "$RESULT" | jq -r '.passed')
echo "verdict=${VERDICT}" >> $GITHUB_OUTPUT
echo "verdict-digest=${VERDICT_DIGEST}" >> $GITHUB_OUTPUT
echo "passed=${PASSED}" >> $GITHUB_OUTPUT
# Save verdict for signing
echo "$RESULT" > verdict.json
- name: Upload Verdict
uses: actions/upload-artifact@v4
with:
name: verdict
path: verdict.json
sign-verdict:
needs: evaluate
uses: ./.github/workflows/examples/stellaops-sign.yml
with:
artifact-digest: ${{ needs.evaluate.outputs.verdict-digest }}
artifact-type: verdict
predicate-type: 'verdict.stella/v1'
push-attestation: true
permissions:
id-token: write
contents: read
packages: write
gate:
needs: [evaluate, sign-verdict]
runs-on: ubuntu-latest
steps:
- name: Check Verdict
run: |
PASSED="${{ needs.evaluate.outputs.passed }}"
VERDICT="${{ needs.evaluate.outputs.verdict }}"
if [[ "$PASSED" != "true" ]]; then
echo "::error::Policy verdict: ${VERDICT}"
echo "::error::Deployment blocked by policy"
exit 1
fi
echo "Policy verdict: ${VERDICT} - Proceeding with deployment"
- name: Summary
run: |
PASSED="${{ needs.evaluate.outputs.passed }}"
if [[ "$PASSED" == "true" ]]; then
ICON="white_check_mark"
STATUS="PASSED"
else
ICON="x"
STATUS="BLOCKED"
fi
cat >> $GITHUB_STEP_SUMMARY << EOF
## :${ICON}: Policy Verdict: ${STATUS}
| Field | Value |
|-------|-------|
| **Image** | \`${{ needs.evaluate.outputs.image-digest }}\` |
| **Verdict** | \`${{ needs.evaluate.outputs.verdict }}\` |
| **Verdict Digest** | \`${{ needs.evaluate.outputs.verdict-digest }}\` |
| **Attestation** | \`${{ needs.sign-verdict.outputs.attestation-digest }}\` |
| **Rekor UUID** | \`${{ needs.sign-verdict.outputs.rekor-uuid }}\` |
### Verify Verdict
\`\`\`bash
stella attest verify \\
--artifact "${{ needs.evaluate.outputs.verdict-digest }}" \\
--certificate-identity "repo:${{ github.repository }}:ref:${{ github.ref }}" \\
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
\`\`\`
EOF
# Example deployment job - only runs if gate passes
deploy:
needs: [evaluate, gate]
if: needs.evaluate.outputs.passed == 'true'
runs-on: ubuntu-latest
environment: production
steps:
- name: Deploy
run: |
echo "Deploying ${{ needs.evaluate.outputs.image-digest }}"
echo "Policy verdict verified and signed"
# Add your deployment commands here