notify doctors work, audit work, new product advisory sprints
This commit is contained in:
371
docs/examples/binary-diff/ci-cd-integration.md
Normal file
371
docs/examples/binary-diff/ci-cd-integration.md
Normal file
@@ -0,0 +1,371 @@
|
||||
# CI/CD Integration
|
||||
|
||||
This example demonstrates how to integrate binary diff attestation into your CI/CD pipelines.
|
||||
|
||||
## GitHub Actions
|
||||
|
||||
### Basic Workflow
|
||||
|
||||
```yaml
|
||||
# .github/workflows/binary-diff.yml
|
||||
name: Binary Diff Attestation
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
binary-diff:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write # For keyless signing
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Stella CLI
|
||||
uses: stellaops/setup-stella@v1
|
||||
with:
|
||||
version: 'latest'
|
||||
|
||||
- name: Login to Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Get Previous Tag
|
||||
id: prev-tag
|
||||
run: |
|
||||
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
|
||||
echo "tag=$PREV_TAG" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Binary Diff
|
||||
if: steps.prev-tag.outputs.tag != ''
|
||||
run: |
|
||||
stella scan diff \
|
||||
--base ghcr.io/${{ github.repository }}:${{ steps.prev-tag.outputs.tag }} \
|
||||
--target ghcr.io/${{ github.repository }}:${{ github.ref_name }} \
|
||||
--mode=elf \
|
||||
--emit-dsse=./attestations/ \
|
||||
--format=json > diff.json
|
||||
|
||||
- name: Upload Attestations
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: binary-diff-attestations
|
||||
path: |
|
||||
attestations/
|
||||
diff.json
|
||||
|
||||
- name: Attach Attestation to Image
|
||||
run: |
|
||||
# Using cosign to attach attestation
|
||||
cosign attach attestation \
|
||||
--attestation ./attestations/linux-amd64-binarydiff.dsse.json \
|
||||
ghcr.io/${{ github.repository }}:${{ github.ref_name }}
|
||||
```
|
||||
|
||||
### With Release Gate
|
||||
|
||||
```yaml
|
||||
# .github/workflows/release-gate.yml
|
||||
name: Release Gate with Binary Diff
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
base_version:
|
||||
description: 'Base version to compare'
|
||||
required: true
|
||||
target_version:
|
||||
description: 'Target version to release'
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
binary-diff-gate:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
verdict: ${{ steps.analyze.outputs.verdict }}
|
||||
|
||||
steps:
|
||||
- name: Setup Stella CLI
|
||||
uses: stellaops/setup-stella@v1
|
||||
|
||||
- name: Binary Diff Analysis
|
||||
id: diff
|
||||
run: |
|
||||
stella scan diff \
|
||||
--base myapp:${{ inputs.base_version }} \
|
||||
--target myapp:${{ inputs.target_version }} \
|
||||
--format=json > diff.json
|
||||
|
||||
- name: Analyze Results
|
||||
id: analyze
|
||||
run: |
|
||||
# Check for unknown verdicts
|
||||
UNKNOWN_COUNT=$(jq '.summary.verdicts.unknown // 0' diff.json)
|
||||
if [ "$UNKNOWN_COUNT" -gt "0" ]; then
|
||||
echo "verdict=review-required" >> $GITHUB_OUTPUT
|
||||
echo "::warning::Found $UNKNOWN_COUNT binaries with unknown verdicts"
|
||||
else
|
||||
echo "verdict=approved" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Gate Decision
|
||||
if: steps.analyze.outputs.verdict == 'review-required'
|
||||
run: |
|
||||
echo "Manual review required for unknown binary changes"
|
||||
exit 1
|
||||
```
|
||||
|
||||
## GitLab CI
|
||||
|
||||
### Basic Pipeline
|
||||
|
||||
```yaml
|
||||
# .gitlab-ci.yml
|
||||
stages:
|
||||
- build
|
||||
- analyze
|
||||
- release
|
||||
|
||||
variables:
|
||||
STELLA_VERSION: "latest"
|
||||
|
||||
binary-diff:
|
||||
stage: analyze
|
||||
image: stellaops/cli:${STELLA_VERSION}
|
||||
script:
|
||||
- |
|
||||
# Get previous tag
|
||||
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
|
||||
|
||||
if [ -n "$PREV_TAG" ]; then
|
||||
stella scan diff \
|
||||
--base ${CI_REGISTRY_IMAGE}:${PREV_TAG} \
|
||||
--target ${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG} \
|
||||
--mode=elf \
|
||||
--emit-dsse=attestations/ \
|
||||
--format=json > diff.json
|
||||
|
||||
# Upload to GitLab artifacts
|
||||
echo "Binary diff completed"
|
||||
else
|
||||
echo "No previous tag found, skipping diff"
|
||||
fi
|
||||
artifacts:
|
||||
paths:
|
||||
- attestations/
|
||||
- diff.json
|
||||
expire_in: 30 days
|
||||
only:
|
||||
- tags
|
||||
```
|
||||
|
||||
### With Security Gate
|
||||
|
||||
```yaml
|
||||
# .gitlab-ci.yml
|
||||
security-gate:
|
||||
stage: analyze
|
||||
image: stellaops/cli:latest
|
||||
script:
|
||||
- |
|
||||
stella scan diff \
|
||||
--base ${CI_REGISTRY_IMAGE}:${BASE_VERSION} \
|
||||
--target ${CI_REGISTRY_IMAGE}:${TARGET_VERSION} \
|
||||
--format=json > diff.json
|
||||
|
||||
# Fail if any unknown verdicts
|
||||
UNKNOWN=$(jq '.summary.verdicts.unknown // 0' diff.json)
|
||||
if [ "$UNKNOWN" -gt "0" ]; then
|
||||
echo "Security gate failed: $UNKNOWN unknown binary changes"
|
||||
jq '.findings[] | select(.verdict == "unknown")' diff.json
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Security gate passed"
|
||||
allow_failure: false
|
||||
```
|
||||
|
||||
## Jenkins Pipeline
|
||||
|
||||
```groovy
|
||||
// Jenkinsfile
|
||||
pipeline {
|
||||
agent any
|
||||
|
||||
environment {
|
||||
STELLA_VERSION = 'latest'
|
||||
}
|
||||
|
||||
stages {
|
||||
stage('Binary Diff') {
|
||||
steps {
|
||||
script {
|
||||
def prevTag = sh(
|
||||
script: 'git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo ""',
|
||||
returnStdout: true
|
||||
).trim()
|
||||
|
||||
if (prevTag) {
|
||||
sh """
|
||||
stella scan diff \\
|
||||
--base ${REGISTRY}/${IMAGE}:${prevTag} \\
|
||||
--target ${REGISTRY}/${IMAGE}:${TAG} \\
|
||||
--mode=elf \\
|
||||
--emit-dsse=attestations/ \\
|
||||
--format=json > diff.json
|
||||
"""
|
||||
|
||||
archiveArtifacts artifacts: 'attestations/*, diff.json'
|
||||
|
||||
// Parse and check results
|
||||
def diff = readJSON file: 'diff.json'
|
||||
if (diff.summary.verdicts.unknown > 0) {
|
||||
unstable("Found ${diff.summary.verdicts.unknown} unknown binary changes")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Azure DevOps
|
||||
|
||||
```yaml
|
||||
# azure-pipelines.yml
|
||||
trigger:
|
||||
tags:
|
||||
include:
|
||||
- v*
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
|
||||
steps:
|
||||
- task: Bash@3
|
||||
displayName: 'Install Stella CLI'
|
||||
inputs:
|
||||
targetType: 'inline'
|
||||
script: |
|
||||
curl -sSL https://get.stellaops.io | sh
|
||||
stella --version
|
||||
|
||||
- task: Docker@2
|
||||
displayName: 'Login to Registry'
|
||||
inputs:
|
||||
containerRegistry: 'myRegistry'
|
||||
command: 'login'
|
||||
|
||||
- task: Bash@3
|
||||
displayName: 'Binary Diff'
|
||||
inputs:
|
||||
targetType: 'inline'
|
||||
script: |
|
||||
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
|
||||
if [ -n "$PREV_TAG" ]; then
|
||||
stella scan diff \
|
||||
--base $(REGISTRY)/$(IMAGE):${PREV_TAG} \
|
||||
--target $(REGISTRY)/$(IMAGE):$(Build.SourceBranchName) \
|
||||
--mode=elf \
|
||||
--emit-dsse=$(Build.ArtifactStagingDirectory)/attestations/ \
|
||||
--format=json > $(Build.ArtifactStagingDirectory)/diff.json
|
||||
fi
|
||||
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs:
|
||||
pathToPublish: '$(Build.ArtifactStagingDirectory)'
|
||||
artifactName: 'binary-diff'
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Always Use Digest References in Production
|
||||
|
||||
```bash
|
||||
# Instead of tags
|
||||
stella scan diff --base myapp:v1.0.0 --target myapp:v1.0.1
|
||||
|
||||
# Use digests for immutability
|
||||
stella scan diff \
|
||||
--base myapp@sha256:abc123... \
|
||||
--target myapp@sha256:def456...
|
||||
```
|
||||
|
||||
### 2. Store Attestations with Releases
|
||||
|
||||
Attach DSSE attestations to your container images or store them alongside release artifacts.
|
||||
|
||||
### 3. Set Appropriate Timeouts
|
||||
|
||||
```bash
|
||||
# For large images, increase timeout
|
||||
stella scan diff \
|
||||
--base myapp:v1 \
|
||||
--target myapp:v2 \
|
||||
--timeout=600
|
||||
```
|
||||
|
||||
### 4. Use Caching
|
||||
|
||||
```yaml
|
||||
# GitHub Actions with caching
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.stella/cache
|
||||
key: stella-${{ runner.os }}-${{ hashFiles('**/Dockerfile') }}
|
||||
```
|
||||
|
||||
### 5. Fail Fast on Critical Issues
|
||||
|
||||
```bash
|
||||
# Exit code indicates issues
|
||||
stella scan diff --base old --target new --format=json > diff.json
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Diff failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for critical verdicts
|
||||
jq -e '.summary.verdicts.unknown == 0' diff.json || exit 1
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Registry Authentication
|
||||
|
||||
```bash
|
||||
# Use Docker config
|
||||
stella scan diff \
|
||||
--base myapp:v1 \
|
||||
--target myapp:v2 \
|
||||
--registry-auth=~/.docker/config.json
|
||||
```
|
||||
|
||||
### Platform Issues
|
||||
|
||||
```bash
|
||||
# Explicitly specify platform for multi-arch
|
||||
stella scan diff \
|
||||
--base myapp:v1 \
|
||||
--target myapp:v2 \
|
||||
--platform=linux/amd64
|
||||
```
|
||||
|
||||
### Timeout Issues
|
||||
|
||||
```bash
|
||||
# Increase timeout for slow registries
|
||||
stella scan diff \
|
||||
--base myapp:v1 \
|
||||
--target myapp:v2 \
|
||||
--timeout=900
|
||||
```
|
||||
Reference in New Issue
Block a user