notify doctors work, audit work, new product advisory sprints

This commit is contained in:
master
2026-01-13 08:36:29 +02:00
parent b8868a5f13
commit 9ca7cb183e
343 changed files with 24492 additions and 3544 deletions

View File

@@ -0,0 +1,55 @@
# Binary Diff Examples
This directory contains examples demonstrating the binary diff attestation feature.
## Prerequisites
- StellaOps CLI (`stella`) installed
- Access to a container registry
- Docker or containerd runtime (for image pulling)
## Examples
### Basic Comparison
[basic-comparison.md](./basic-comparison.md) - Simple binary diff between two image versions
### DSSE Attestation
[dsse-attestation.md](./dsse-attestation.md) - Generating and verifying DSSE-signed attestations
### Policy Integration
[policy-integration.md](./policy-integration.md) - Using binary diff evidence in policy rules
### CI/CD Integration
[ci-cd-integration.md](./ci-cd-integration.md) - GitHub Actions and GitLab CI examples
## Sample Outputs
The `sample-outputs/` directory contains:
- `diff-table.txt` - Sample table-formatted output
- `diff.json` - Sample JSON output
- `attestation.dsse.json` - Sample DSSE envelope
## Quick Start
```bash
# Compare two image versions
stella scan diff --base myapp:1.0.0 --target myapp:1.0.1
# Generate attestation
stella scan diff --base myapp:1.0.0 --target myapp:1.0.1 \
--mode=elf --emit-dsse=./attestations/
# Verify attestation
stella verify attestation ./attestations/linux-amd64-binarydiff.dsse.json
```
## Related Documentation
- [Binary Diff Attestation Architecture](../../modules/scanner/binary-diff-attestation.md)
- [BinaryDiffV1 JSON Schema](../../schemas/binarydiff-v1.schema.json)
- [CLI Reference](../../API_CLI_REFERENCE.md#stella-scan-diff)

View File

@@ -0,0 +1,143 @@
# Basic Binary Comparison
This example demonstrates how to perform a basic binary diff between two container image versions.
## Scenario
You have deployed `myapp:1.0.0` and want to understand what binary changes are in `myapp:1.0.1` before upgrading.
## Prerequisites
```bash
# Ensure stella CLI is installed
stella --version
# Verify registry access
stella registry ping docker://registry.example.com
```
## Basic Comparison
### Table Output (Default)
```bash
stella scan diff \
--base docker://registry.example.com/myapp:1.0.0 \
--target docker://registry.example.com/myapp:1.0.1
```
Output:
```
Binary Diff: docker://registry.example.com/myapp:1.0.0 -> docker://registry.example.com/myapp:1.0.1
Platform: linux/amd64
Analysis Mode: ELF Section Hashes
PATH CHANGE VERDICT CONFIDENCE
--------------------------------------------------------------------------------
/usr/lib/libssl.so.3 modified patched 0.95
/usr/lib/libcrypto.so.3 modified patched 0.92
/app/bin/myapp modified vanilla 0.98
Summary: 156 binaries analyzed, 3 modified, 153 unchanged
```
### JSON Output
```bash
stella scan diff \
--base docker://registry.example.com/myapp:1.0.0 \
--target docker://registry.example.com/myapp:1.0.1 \
--format=json > diff.json
```
The JSON output contains detailed section-level information. See [sample-outputs/diff.json](./sample-outputs/diff.json) for a complete example.
### Summary Output
```bash
stella scan diff \
--base docker://registry.example.com/myapp:1.0.0 \
--target docker://registry.example.com/myapp:1.0.1 \
--format=summary
```
Output:
```
Binary Diff Summary
-------------------
Base: docker://registry.example.com/myapp:1.0.0 (sha256:abc123...)
Target: docker://registry.example.com/myapp:1.0.1 (sha256:def456...)
Platform: linux/amd64
Binaries: 156 total, 3 modified, 153 unchanged
Verdicts: 2 patched, 1 vanilla
```
## Using Digest References
For immutable references, use digests instead of tags:
```bash
stella scan diff \
--base docker://registry.example.com/myapp@sha256:abc123... \
--target docker://registry.example.com/myapp@sha256:def456...
```
## Filtering by Platform
For multi-arch images, specify the platform:
```bash
# Linux AMD64 only
stella scan diff \
--base myapp:1.0.0 \
--target myapp:1.0.1 \
--platform=linux/amd64
# Linux ARM64
stella scan diff \
--base myapp:1.0.0 \
--target myapp:1.0.1 \
--platform=linux/arm64
```
## Including Unchanged Binaries
By default, unchanged binaries are excluded from output. To include them:
```bash
stella scan diff \
--base myapp:1.0.0 \
--target myapp:1.0.1 \
--include-unchanged
```
## Verbose Output
For debugging or detailed progress:
```bash
stella scan diff \
--base myapp:1.0.0 \
--target myapp:1.0.1 \
--verbose
```
Output includes:
- Layer download progress
- Binary detection details
- Section hash computation progress
## Understanding Verdicts
| Verdict | Meaning | Action |
|---------|---------|--------|
| `patched` | High confidence that a security patch was applied | Review changelog, consider safe to upgrade |
| `vanilla` | Standard code change, no backport evidence | Normal release update |
| `unknown` | Cannot determine patch status | Manual review recommended |
## Next Steps
- [Generate DSSE Attestations](./dsse-attestation.md) for audit trail
- [Integrate with Policy](./policy-integration.md) for automated gates
- [Add to CI/CD](./ci-cd-integration.md) for continuous verification

View 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
```

View File

@@ -0,0 +1,17 @@
{
"payloadType": "stellaops.binarydiff.v1",
"payload": "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEiLCJzdWJqZWN0IjpbeyJuYW1lIjoiZG9ja2VyOi8vcmVnaXN0cnkuZXhhbXBsZS5jb20vYXBwQHNoYTI1NjpkZWY0NTZhYmM3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1NjdlZmdoIiwiZGlnZXN0Ijp7InNoYTI1NiI6ImRlZjQ1NmFiYzc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2N2VmZ2gifX1dLCJwcmVkaWNhdGVUeXBlIjoic3RlbGxhb3BzLmJpbmFyeWRpZmYudjEiLCJwcmVkaWNhdGUiOnsiaW5wdXRzIjp7ImJhc2UiOnsiZGlnZXN0Ijoic2hhMjU2OmFiYzEyM2RlZjQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNGFiY2QifSwidGFyZ2V0Ijp7ImRpZ2VzdCI6InNoYTI1NjpkZWY0NTZhYmM3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1NjdlZmdoIn19LCJmaW5kaW5ncyI6W3sicGF0aCI6Ii91c3IvbGliL2xpYnNzbC5zby4zIiwiY2hhbmdlVHlwZSI6Im1vZGlmaWVkIiwidmVyZGljdCI6InBhdGNoZWQiLCJjb25maWRlbmNlIjowLjk1fV0sIm1ldGFkYXRhIjp7InRvb2xWZXJzaW9uIjoiMS4wLjAiLCJhbmFseXNpc1RpbWVzdGFtcCI6IjIwMjYtMDEtMTNUMTI6MDA6MDBaIn19fQ==",
"signatures": [
{
"keyid": "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA",
"sig": "MEUCIQDKZokqnCjrRtw5EXP14JvsBwFDRPfCp9K0UoOlWGdlDQIgSNpOGPqKNLv5MNZLYc5iE7q5b3wW6K0cDpjNxBxCWdU="
}
],
"_note": "This is a sample DSSE envelope for documentation purposes. The payload is base64-encoded and contains an in-toto statement with a BinaryDiffV1 predicate. In production, the signature would be cryptographically valid.",
"_rekorMetadata": {
"logIndex": 12345678,
"entryUuid": "24296fb24b8ad77aa3e6b0d1b6e0e3a0c9f8d7e6b5a4c3d2e1f0a9b8c7d6e5f4",
"integratedTime": "2026-01-13T12:00:05Z",
"logUrl": "https://rekor.sigstore.dev"
}
}

View File

@@ -0,0 +1,27 @@
Binary Diff: docker://registry.example.com/app:1.0.0 -> docker://registry.example.com/app:1.0.1
Platform: linux/amd64
Analysis Mode: ELF Section Hashes
Analyzed Sections: .text, .rodata, .data, .symtab, .dynsym
PATH CHANGE VERDICT CONFIDENCE SECTIONS CHANGED
--------------------------------------------------------------------------------------------------
/usr/lib/x86_64-linux-gnu/libssl.so.3 modified patched 0.95 .text, .rodata
/usr/lib/x86_64-linux-gnu/libcrypto.so.3 modified patched 0.92 .text
/usr/bin/openssl modified unknown 0.75 .text, .data, .symtab
/lib/x86_64-linux-gnu/libc.so.6 unchanged - - -
/lib/x86_64-linux-gnu/libpthread.so.0 unchanged - - -
/usr/lib/x86_64-linux-gnu/libz.so.1 unchanged - - -
/app/bin/myapp modified vanilla 0.98 .text, .rodata, .data
Summary
-------
Total binaries analyzed: 156
Modified: 4
Unchanged: 152
Verdicts:
Patched: 2 (high confidence backport detected)
Vanilla: 1 (standard update, no backport evidence)
Unknown: 1 (insufficient evidence for classification)
Analysis completed in 12.4s

View File

@@ -0,0 +1,179 @@
{
"schemaVersion": "1.0.0",
"base": {
"reference": "docker://registry.example.com/app:1.0.0",
"digest": "sha256:abc123def456789012345678901234567890123456789012345678901234abcd",
"manifestDigest": "sha256:111222333444555666777888999000aaabbbcccdddeeefff000111222333444555"
},
"target": {
"reference": "docker://registry.example.com/app:1.0.1",
"digest": "sha256:def456abc789012345678901234567890123456789012345678901234567efgh",
"manifestDigest": "sha256:666777888999000aaabbbcccdddeeefff000111222333444555666777888999000"
},
"platform": {
"os": "linux",
"architecture": "amd64"
},
"analysisMode": "elf",
"timestamp": "2026-01-13T12:00:00.000000Z",
"findings": [
{
"path": "/usr/lib/x86_64-linux-gnu/libssl.so.3",
"changeType": "modified",
"binaryFormat": "elf",
"layerDigest": "sha256:aaa111bbb222ccc333ddd444eee555fff666777888999000aaabbbcccdddeeef",
"baseHashes": {
"buildId": "abc123def456789012345678",
"fileHash": "1111111111111111111111111111111111111111111111111111111111111111",
"sections": {
".text": {
"sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"size": 524288,
"offset": 4096
},
".rodata": {
"sha256": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"size": 131072,
"offset": 528384
}
}
},
"targetHashes": {
"buildId": "def789abc012345678901234",
"fileHash": "2222222222222222222222222222222222222222222222222222222222222222",
"sections": {
".text": {
"sha256": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
"size": 524544,
"offset": 4096
},
".rodata": {
"sha256": "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
"size": 131200,
"offset": 528640
}
}
},
"sectionDeltas": [
{
"section": ".text",
"status": "modified",
"baseSha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"targetSha256": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
"sizeDelta": 256
},
{
"section": ".rodata",
"status": "modified",
"baseSha256": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"targetSha256": "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
"sizeDelta": 128
},
{
"section": ".data",
"status": "identical",
"baseSha256": "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"targetSha256": "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"sizeDelta": 0
},
{
"section": ".symtab",
"status": "identical",
"baseSha256": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"targetSha256": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"sizeDelta": 0
}
],
"confidence": 0.95,
"verdict": "patched"
},
{
"path": "/usr/lib/x86_64-linux-gnu/libcrypto.so.3",
"changeType": "modified",
"binaryFormat": "elf",
"layerDigest": "sha256:aaa111bbb222ccc333ddd444eee555fff666777888999000aaabbbcccdddeeef",
"sectionDeltas": [
{
"section": ".text",
"status": "modified",
"sizeDelta": 1024
},
{
"section": ".rodata",
"status": "identical",
"sizeDelta": 0
}
],
"confidence": 0.92,
"verdict": "patched"
},
{
"path": "/usr/bin/openssl",
"changeType": "modified",
"binaryFormat": "elf",
"sectionDeltas": [
{
"section": ".text",
"status": "modified",
"sizeDelta": 512
},
{
"section": ".data",
"status": "modified",
"sizeDelta": 64
},
{
"section": ".symtab",
"status": "modified",
"sizeDelta": 128
}
],
"confidence": 0.75,
"verdict": "unknown"
},
{
"path": "/app/bin/myapp",
"changeType": "modified",
"binaryFormat": "elf",
"sectionDeltas": [
{
"section": ".text",
"status": "modified",
"sizeDelta": 2048
},
{
"section": ".rodata",
"status": "modified",
"sizeDelta": 512
},
{
"section": ".data",
"status": "modified",
"sizeDelta": 128
}
],
"confidence": 0.98,
"verdict": "vanilla"
}
],
"summary": {
"totalBinaries": 156,
"modified": 4,
"unchanged": 152,
"added": 0,
"removed": 0,
"verdicts": {
"patched": 2,
"vanilla": 1,
"unknown": 1,
"incompatible": 0
},
"sectionsAnalyzed": [".text", ".rodata", ".data", ".symtab", ".dynsym"],
"analysisDurationMs": 12400
},
"metadata": {
"toolVersion": "1.0.0",
"analysisTimestamp": "2026-01-13T12:00:00.000000Z",
"configDigest": "sha256:config123456789abcdef0123456789abcdef0123456789abcdef0123456789ab"
}
}