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.
This commit is contained in:
216
.github/workflows/examples/stellaops-sign.yml
vendored
Normal file
216
.github/workflows/examples/stellaops-sign.yml
vendored
Normal file
@@ -0,0 +1,216 @@
|
||||
# .github/workflows/examples/stellaops-sign.yml
|
||||
# StellaOps Keyless Sign Reusable Workflow
|
||||
#
|
||||
# This reusable workflow enables keyless signing of artifacts using Sigstore Fulcio.
|
||||
# It uses OIDC identity tokens from GitHub Actions to obtain ephemeral signing certificates.
|
||||
#
|
||||
# Usage:
|
||||
# jobs:
|
||||
# sign:
|
||||
# uses: stella-ops/templates/.github/workflows/stellaops-sign.yml@v1
|
||||
# with:
|
||||
# artifact-digest: sha256:abc123...
|
||||
# artifact-type: image
|
||||
# permissions:
|
||||
# id-token: write
|
||||
# contents: read
|
||||
#
|
||||
# Prerequisites:
|
||||
# - StellaOps API accessible from runner
|
||||
# - OIDC token permissions granted
|
||||
#
|
||||
# See: docs/modules/signer/guides/keyless-signing.md
|
||||
|
||||
name: StellaOps Keyless Sign
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
artifact-digest:
|
||||
description: 'SHA256 digest of artifact to sign (e.g., sha256:abc123...)'
|
||||
required: true
|
||||
type: string
|
||||
artifact-type:
|
||||
description: 'Type of artifact: image, sbom, verdict, report'
|
||||
required: false
|
||||
type: string
|
||||
default: 'image'
|
||||
stellaops-url:
|
||||
description: 'StellaOps API URL'
|
||||
required: false
|
||||
type: string
|
||||
default: 'https://api.stella-ops.org'
|
||||
push-attestation:
|
||||
description: 'Push attestation to OCI registry'
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
predicate-type:
|
||||
description: 'Custom predicate type URI (optional)'
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
include-rekor:
|
||||
description: 'Log signature to Rekor transparency log'
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
cli-version:
|
||||
description: 'StellaOps CLI version to use'
|
||||
required: false
|
||||
type: string
|
||||
default: 'latest'
|
||||
outputs:
|
||||
attestation-digest:
|
||||
description: 'Digest of created attestation'
|
||||
value: ${{ jobs.sign.outputs.attestation-digest }}
|
||||
rekor-uuid:
|
||||
description: 'Rekor transparency log UUID (if logged)'
|
||||
value: ${{ jobs.sign.outputs.rekor-uuid }}
|
||||
certificate-identity:
|
||||
description: 'OIDC identity bound to certificate'
|
||||
value: ${{ jobs.sign.outputs.certificate-identity }}
|
||||
signed-at:
|
||||
description: 'Signing timestamp (UTC ISO-8601)'
|
||||
value: ${{ jobs.sign.outputs.signed-at }}
|
||||
|
||||
jobs:
|
||||
sign:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write # Required for OIDC token
|
||||
contents: read # Required for checkout
|
||||
packages: write # Required if pushing to GHCR
|
||||
|
||||
outputs:
|
||||
attestation-digest: ${{ steps.sign.outputs.attestation-digest }}
|
||||
rekor-uuid: ${{ steps.sign.outputs.rekor-uuid }}
|
||||
certificate-identity: ${{ steps.sign.outputs.certificate-identity }}
|
||||
signed-at: ${{ steps.sign.outputs.signed-at }}
|
||||
|
||||
steps:
|
||||
- name: Validate Inputs
|
||||
run: |
|
||||
if [[ ! "${{ inputs.artifact-digest }}" =~ ^sha256:[a-f0-9]{64}$ ]] && \
|
||||
[[ ! "${{ inputs.artifact-digest }}" =~ ^sha512:[a-f0-9]{128}$ ]]; then
|
||||
echo "::error::Invalid artifact-digest format. Expected sha256:... or sha512:..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VALID_TYPES="image sbom verdict report binary"
|
||||
if [[ ! " $VALID_TYPES " =~ " ${{ inputs.artifact-type }} " ]]; then
|
||||
echo "::error::Invalid artifact-type. Must be one of: $VALID_TYPES"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Install StellaOps CLI
|
||||
uses: stella-ops/setup-cli@v1
|
||||
with:
|
||||
version: ${{ inputs.cli-version }}
|
||||
|
||||
- name: Get OIDC Token
|
||||
id: oidc
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
# Request OIDC token with sigstore audience
|
||||
OIDC_TOKEN=$(curl -sLS "${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=sigstore" \
|
||||
-H "Authorization: bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" \
|
||||
| jq -r '.value')
|
||||
|
||||
if [[ -z "$OIDC_TOKEN" || "$OIDC_TOKEN" == "null" ]]; then
|
||||
echo "::error::Failed to obtain OIDC token"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Mask token in logs
|
||||
echo "::add-mask::${OIDC_TOKEN}"
|
||||
echo "token=${OIDC_TOKEN}" >> $GITHUB_OUTPUT
|
||||
|
||||
# Extract identity for logging (non-sensitive)
|
||||
IDENTITY=$(echo "$OIDC_TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | jq -r '.sub // "unknown"' 2>/dev/null || echo "unknown")
|
||||
echo "identity=${IDENTITY}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Keyless Sign
|
||||
id: sign
|
||||
env:
|
||||
STELLAOPS_OIDC_TOKEN: ${{ steps.oidc.outputs.token }}
|
||||
STELLAOPS_URL: ${{ inputs.stellaops-url }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
SIGN_ARGS=(
|
||||
--keyless
|
||||
--artifact "${{ inputs.artifact-digest }}"
|
||||
--type "${{ inputs.artifact-type }}"
|
||||
--output json
|
||||
)
|
||||
|
||||
# Add optional predicate type
|
||||
if [[ -n "${{ inputs.predicate-type }}" ]]; then
|
||||
SIGN_ARGS+=(--predicate-type "${{ inputs.predicate-type }}")
|
||||
fi
|
||||
|
||||
# Add Rekor logging option
|
||||
if [[ "${{ inputs.include-rekor }}" == "true" ]]; then
|
||||
SIGN_ARGS+=(--rekor)
|
||||
fi
|
||||
|
||||
echo "::group::Signing artifact"
|
||||
RESULT=$(stella attest sign "${SIGN_ARGS[@]}")
|
||||
echo "$RESULT" | jq .
|
||||
echo "::endgroup::"
|
||||
|
||||
# Extract outputs
|
||||
ATTESTATION_DIGEST=$(echo "$RESULT" | jq -r '.attestationDigest // empty')
|
||||
REKOR_UUID=$(echo "$RESULT" | jq -r '.rekorUuid // empty')
|
||||
CERT_IDENTITY=$(echo "$RESULT" | jq -r '.certificateIdentity // empty')
|
||||
SIGNED_AT=$(echo "$RESULT" | jq -r '.signedAt // empty')
|
||||
|
||||
if [[ -z "$ATTESTATION_DIGEST" ]]; then
|
||||
echo "::error::Signing failed - no attestation digest returned"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "attestation-digest=${ATTESTATION_DIGEST}" >> $GITHUB_OUTPUT
|
||||
echo "rekor-uuid=${REKOR_UUID}" >> $GITHUB_OUTPUT
|
||||
echo "certificate-identity=${CERT_IDENTITY}" >> $GITHUB_OUTPUT
|
||||
echo "signed-at=${SIGNED_AT}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Push Attestation
|
||||
if: ${{ inputs.push-attestation }}
|
||||
env:
|
||||
STELLAOPS_URL: ${{ inputs.stellaops-url }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
echo "::group::Pushing attestation to registry"
|
||||
stella attest push \
|
||||
--attestation "${{ steps.sign.outputs.attestation-digest }}" \
|
||||
--registry "${{ github.repository }}"
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Generate Summary
|
||||
run: |
|
||||
cat >> $GITHUB_STEP_SUMMARY << 'EOF'
|
||||
## Attestation Created
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Artifact** | `${{ inputs.artifact-digest }}` |
|
||||
| **Type** | `${{ inputs.artifact-type }}` |
|
||||
| **Attestation** | `${{ steps.sign.outputs.attestation-digest }}` |
|
||||
| **Rekor UUID** | `${{ steps.sign.outputs.rekor-uuid || 'N/A' }}` |
|
||||
| **Certificate Identity** | `${{ steps.sign.outputs.certificate-identity }}` |
|
||||
| **Signed At** | `${{ steps.sign.outputs.signed-at }}` |
|
||||
| **Signing Mode** | Keyless (Fulcio) |
|
||||
|
||||
### Verification Command
|
||||
|
||||
```bash
|
||||
stella attest verify \
|
||||
--artifact "${{ inputs.artifact-digest }}" \
|
||||
--certificate-identity "${{ steps.sign.outputs.certificate-identity }}" \
|
||||
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
|
||||
```
|
||||
EOF
|
||||
Reference in New Issue
Block a user