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:
StellaOps Bot
2025-12-26 15:17:15 +02:00
parent 7792749bb4
commit 907783f625
354 changed files with 79727 additions and 1346 deletions

View File

@@ -0,0 +1,305 @@
# deploy/gitlab/examples/.gitlab-ci-stellaops.yml
# StellaOps Keyless Signing Templates for GitLab CI
#
# Include this file in your .gitlab-ci.yml to enable keyless signing:
#
# include:
# - project: 'stella-ops/templates'
# file: 'deploy/gitlab/examples/.gitlab-ci-stellaops.yml'
#
# sign-image:
# extends: .stellaops-sign
# variables:
# ARTIFACT_DIGEST: $CI_REGISTRY_IMAGE@sha256:...
# ARTIFACT_TYPE: image
#
# See: docs/modules/signer/guides/keyless-signing.md
# ==============================================================================
# Base Configuration
# ==============================================================================
variables:
STELLAOPS_URL: "https://api.stella-ops.org"
STELLAOPS_CLI_VERSION: "latest"
# ==============================================================================
# Keyless Signing Job Template
# ==============================================================================
.stellaops-sign:
image: stella-ops/cli:${STELLAOPS_CLI_VERSION}
id_tokens:
STELLAOPS_OIDC_TOKEN:
aud: sigstore
variables:
# Required - must be set by extending job
ARTIFACT_DIGEST: ""
# Optional - defaults to 'image'
ARTIFACT_TYPE: "image"
# Optional - include in Rekor transparency log
INCLUDE_REKOR: "true"
# Optional - push attestation to registry
PUSH_ATTESTATION: "true"
before_script:
- |
if [[ -z "${ARTIFACT_DIGEST}" ]]; then
echo "ERROR: ARTIFACT_DIGEST must be set"
exit 1
fi
script:
- |
set -euo pipefail
SIGN_ARGS=(
--keyless
--artifact "${ARTIFACT_DIGEST}"
--type "${ARTIFACT_TYPE}"
--output json
)
if [[ "${INCLUDE_REKOR}" == "true" ]]; then
SIGN_ARGS+=(--rekor)
fi
echo "Signing artifact: ${ARTIFACT_DIGEST}"
RESULT=$(stella attest sign "${SIGN_ARGS[@]}")
# Extract outputs for downstream jobs
ATTESTATION_DIGEST=$(echo "$RESULT" | jq -r '.attestationDigest')
REKOR_UUID=$(echo "$RESULT" | jq -r '.rekorUuid // empty')
CERT_IDENTITY=$(echo "$RESULT" | jq -r '.certificateIdentity // empty')
echo "ATTESTATION_DIGEST=${ATTESTATION_DIGEST}" >> sign.env
echo "REKOR_UUID=${REKOR_UUID}" >> sign.env
echo "CERTIFICATE_IDENTITY=${CERT_IDENTITY}" >> sign.env
echo "Attestation created: ${ATTESTATION_DIGEST}"
if [[ -n "${REKOR_UUID}" ]]; then
echo "Rekor UUID: ${REKOR_UUID}"
fi
# Push attestation if requested
if [[ "${PUSH_ATTESTATION}" == "true" ]]; then
echo "Pushing attestation to registry..."
stella attest push \
--attestation "${ATTESTATION_DIGEST}" \
--registry "${CI_REGISTRY_IMAGE}"
fi
artifacts:
reports:
dotenv: sign.env
# ==============================================================================
# Verification Job Template
# ==============================================================================
.stellaops-verify:
image: stella-ops/cli:${STELLAOPS_CLI_VERSION}
variables:
# Required - must be set by extending job
ARTIFACT_DIGEST: ""
CERTIFICATE_IDENTITY: ""
CERTIFICATE_OIDC_ISSUER: "https://gitlab.com"
# Optional - verification settings
REQUIRE_REKOR: "true"
STRICT: "true"
REQUIRE_SBOM: "false"
REQUIRE_VERDICT: "false"
before_script:
- |
if [[ -z "${ARTIFACT_DIGEST}" ]]; then
echo "ERROR: ARTIFACT_DIGEST must be set"
exit 1
fi
if [[ -z "${CERTIFICATE_IDENTITY}" ]]; then
echo "ERROR: CERTIFICATE_IDENTITY must be set"
exit 1
fi
script:
- |
set -euo pipefail
VERIFY_ARGS=(
--artifact "${ARTIFACT_DIGEST}"
--certificate-identity "${CERTIFICATE_IDENTITY}"
--certificate-oidc-issuer "${CERTIFICATE_OIDC_ISSUER}"
--output json
)
if [[ "${REQUIRE_REKOR}" == "true" ]]; then
VERIFY_ARGS+=(--require-rekor)
fi
if [[ "${REQUIRE_SBOM}" == "true" ]]; then
VERIFY_ARGS+=(--require-sbom)
fi
if [[ "${REQUIRE_VERDICT}" == "true" ]]; then
VERIFY_ARGS+=(--require-verdict)
fi
echo "Verifying artifact: ${ARTIFACT_DIGEST}"
echo "Expected identity: ${CERTIFICATE_IDENTITY}"
set +e
RESULT=$(stella attest verify "${VERIFY_ARGS[@]}" 2>&1)
EXIT_CODE=$?
set -e
VERIFIED=$(echo "$RESULT" | jq -r '.valid // false')
ATTESTATION_COUNT=$(echo "$RESULT" | jq -r '.attestationCount // 0')
echo "VERIFIED=${VERIFIED}" >> verify.env
echo "ATTESTATION_COUNT=${ATTESTATION_COUNT}" >> verify.env
echo "Verified: ${VERIFIED}"
echo "Attestations found: ${ATTESTATION_COUNT}"
if [[ "$VERIFIED" != "true" ]]; then
echo "Verification issues:"
echo "$RESULT" | jq -r '.issues[]? | " - \(.code): \(.message)"'
if [[ "${STRICT}" == "true" ]]; then
echo "ERROR: Verification failed in strict mode"
exit 1
fi
fi
artifacts:
reports:
dotenv: verify.env
# ==============================================================================
# SBOM Generation and Signing Template
# ==============================================================================
.stellaops-sbom:
image: stella-ops/cli:${STELLAOPS_CLI_VERSION}
id_tokens:
STELLAOPS_OIDC_TOKEN:
aud: sigstore
variables:
# Required - image to generate SBOM for
IMAGE: ""
# Optional - SBOM format
SBOM_FORMAT: "cyclonedx-json"
# Optional - output file
SBOM_OUTPUT: "sbom.json"
before_script:
- |
if [[ -z "${IMAGE}" ]]; then
echo "ERROR: IMAGE must be set"
exit 1
fi
script:
- |
set -euo pipefail
echo "Generating SBOM for: ${IMAGE}"
# Generate SBOM
stella sbom generate \
--image "${IMAGE}" \
--format "${SBOM_FORMAT}" \
--output "${SBOM_OUTPUT}"
# Calculate digest
SBOM_DIGEST="sha256:$(sha256sum "${SBOM_OUTPUT}" | cut -d' ' -f1)"
echo "SBOM digest: ${SBOM_DIGEST}"
# Sign SBOM
echo "Signing SBOM..."
RESULT=$(stella attest sign \
--keyless \
--artifact "${SBOM_DIGEST}" \
--type sbom \
--rekor \
--output json)
ATTESTATION_DIGEST=$(echo "$RESULT" | jq -r '.attestationDigest')
REKOR_UUID=$(echo "$RESULT" | jq -r '.rekorUuid // empty')
echo "SBOM_DIGEST=${SBOM_DIGEST}" >> sbom.env
echo "SBOM_ATTESTATION_DIGEST=${ATTESTATION_DIGEST}" >> sbom.env
echo "SBOM_REKOR_UUID=${REKOR_UUID}" >> sbom.env
# Attach to image
echo "Attaching SBOM to image..."
stella attest attach \
--image "${IMAGE}" \
--attestation "${ATTESTATION_DIGEST}" \
--type sbom
echo "SBOM signed and attached successfully"
artifacts:
paths:
- ${SBOM_OUTPUT}
reports:
dotenv: sbom.env
# ==============================================================================
# Policy Verdict Template
# ==============================================================================
.stellaops-verdict:
image: stella-ops/cli:${STELLAOPS_CLI_VERSION}
id_tokens:
STELLAOPS_OIDC_TOKEN:
aud: sigstore
variables:
# Required - image to evaluate
IMAGE: ""
# Optional - policy pack ID
POLICY: "default"
# Optional - fail on block verdict
FAIL_ON_BLOCK: "true"
before_script:
- |
if [[ -z "${IMAGE}" ]]; then
echo "ERROR: IMAGE must be set"
exit 1
fi
script:
- |
set -euo pipefail
echo "Evaluating policy '${POLICY}' for: ${IMAGE}"
RESULT=$(stella policy evaluate \
--image "${IMAGE}" \
--policy "${POLICY}" \
--output json)
VERDICT=$(echo "$RESULT" | jq -r '.verdict')
VERDICT_DIGEST=$(echo "$RESULT" | jq -r '.verdictDigest')
PASSED=$(echo "$RESULT" | jq -r '.passed')
echo "Verdict: ${VERDICT}"
echo "Passed: ${PASSED}"
# Sign verdict
echo "Signing verdict..."
SIGN_RESULT=$(stella attest sign \
--keyless \
--artifact "${VERDICT_DIGEST}" \
--type verdict \
--rekor \
--output json)
ATTESTATION_DIGEST=$(echo "$SIGN_RESULT" | jq -r '.attestationDigest')
REKOR_UUID=$(echo "$SIGN_RESULT" | jq -r '.rekorUuid // empty')
echo "VERDICT=${VERDICT}" >> verdict.env
echo "VERDICT_DIGEST=${VERDICT_DIGEST}" >> verdict.env
echo "VERDICT_PASSED=${PASSED}" >> verdict.env
echo "VERDICT_ATTESTATION_DIGEST=${ATTESTATION_DIGEST}" >> verdict.env
echo "VERDICT_REKOR_UUID=${REKOR_UUID}" >> verdict.env
# Check if we should fail
if [[ "${PASSED}" != "true" && "${FAIL_ON_BLOCK}" == "true" ]]; then
echo "ERROR: Policy verdict is ${VERDICT} - blocking deployment"
exit 1
fi
artifacts:
reports:
dotenv: verdict.env

View File

@@ -0,0 +1,195 @@
# deploy/gitlab/examples/example-pipeline.gitlab-ci.yml
# Example GitLab CI pipeline with StellaOps keyless signing
#
# This example demonstrates:
# - Building and pushing a container image
# - Generating and signing SBOM
# - Evaluating and signing policy verdict
# - Verification gate before deployment
#
# To use, copy this file to your repository's .gitlab-ci.yml
include:
- local: 'deploy/gitlab/examples/.gitlab-ci-stellaops.yml'
# Or include from StellaOps templates project:
# - project: 'stella-ops/templates'
# file: 'deploy/gitlab/examples/.gitlab-ci-stellaops.yml'
stages:
- build
- scan
- sign
- verify
- deploy
variables:
DOCKER_TLS_CERTDIR: "/certs"
IMAGE: ${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHORT_SHA}
# ==============================================================================
# Build Stage
# ==============================================================================
build:
stage: build
image: docker:24
services:
- docker:24-dind
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- |
docker build -t ${IMAGE} .
docker push ${IMAGE}
# Get digest
DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' ${IMAGE} | cut -d@ -f2)
echo "IMAGE_DIGEST=${DIGEST}" >> build.env
echo "IMAGE_REF=${CI_REGISTRY_IMAGE}@${DIGEST}" >> build.env
artifacts:
reports:
dotenv: build.env
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_TAG
# ==============================================================================
# Scan Stage
# ==============================================================================
generate-sbom:
stage: scan
extends: .stellaops-sbom
needs:
- build
variables:
IMAGE: ${IMAGE_REF}
SBOM_FORMAT: "cyclonedx-json"
SBOM_OUTPUT: "sbom.cdx.json"
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_TAG
vulnerability-scan:
stage: scan
image: stella-ops/cli:latest
needs:
- build
script:
- |
stella scan vulnerability \
--image "${IMAGE_REF}" \
--output json > vulnerabilities.json
# Extract summary
CRITICAL=$(jq '.summary.critical // 0' vulnerabilities.json)
HIGH=$(jq '.summary.high // 0' vulnerabilities.json)
echo "Critical: ${CRITICAL}, High: ${HIGH}"
if [[ "${CRITICAL}" -gt 0 ]]; then
echo "WARNING: ${CRITICAL} critical vulnerabilities found"
fi
artifacts:
paths:
- vulnerabilities.json
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_TAG
# ==============================================================================
# Sign Stage
# ==============================================================================
sign-image:
stage: sign
extends: .stellaops-sign
needs:
- build
variables:
ARTIFACT_DIGEST: ${IMAGE_DIGEST}
ARTIFACT_TYPE: "image"
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_TAG
evaluate-policy:
stage: sign
extends: .stellaops-verdict
needs:
- build
- vulnerability-scan
variables:
IMAGE: ${IMAGE_REF}
POLICY: "production"
FAIL_ON_BLOCK: "false" # Don't fail here, let verify stage handle it
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_TAG
# ==============================================================================
# Verify Stage
# ==============================================================================
verify-for-deployment:
stage: verify
extends: .stellaops-verify
needs:
- build
- sign-image
- generate-sbom
- evaluate-policy
variables:
ARTIFACT_DIGEST: ${IMAGE_DIGEST}
CERTIFICATE_IDENTITY: "project_path:${CI_PROJECT_PATH}:ref_type:branch:ref:${CI_COMMIT_REF_NAME}"
CERTIFICATE_OIDC_ISSUER: "${CI_SERVER_URL}"
REQUIRE_SBOM: "true"
REQUIRE_VERDICT: "true"
STRICT: "true"
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_TAG
# ==============================================================================
# Deploy Stage
# ==============================================================================
deploy-staging:
stage: deploy
needs:
- build
- verify-for-deployment
environment:
name: staging
url: https://staging.example.com
script:
- |
echo "Deploying ${IMAGE_REF} to staging"
echo "All attestations verified:"
echo " - Image signature: ${ATTESTATION_DIGEST}"
echo " - SBOM: ${SBOM_ATTESTATION_DIGEST}"
echo " - Policy verdict: ${VERDICT_ATTESTATION_DIGEST}"
# Add your deployment commands here
# kubectl set image deployment/app app=${IMAGE_REF}
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
deploy-production:
stage: deploy
needs:
- build
- verify-for-deployment
- deploy-staging
environment:
name: production
url: https://example.com
script:
- |
echo "Deploying ${IMAGE_REF} to production"
echo "Policy verdict: ${VERDICT}"
# Add your deployment commands here
rules:
- if: $CI_COMMIT_TAG
when: manual