audit, advisories and doctors/setup work

This commit is contained in:
master
2026-01-13 18:53:39 +02:00
parent 9ca7cb183e
commit d7be6ba34b
811 changed files with 54242 additions and 4056 deletions

View File

@@ -42,10 +42,19 @@ 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/
--mode=elf --emit-dsse=./attestations \
--signing-key=./keys/binarydiff.pem
# Verify attestation
stella verify attestation ./attestations/linux-amd64-binarydiff.dsse.json
# Attach attestation to the image
stella attest attach \
--image docker://myapp:1.0.1 \
--attestation ./attestations/linux-amd64-binarydiff.dsse.json
# Verify attestation (example with cosign)
cosign verify-attestation \
--type stellaops.binarydiff.v1 \
--key ./keys/binarydiff.pub \
docker://myapp:1.0.1
```
## Related Documentation

View File

@@ -30,15 +30,17 @@ 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
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
PATH CHANGE VERDICT CONFIDENCE SECTIONS CHANGED
-----------------------------------------------------------------------------------
/app/bin/myapp modified unknown 0.65 .rodata, .text
/usr/lib/libcrypto.so.3 modified unknown 0.70 .text
/usr/lib/libssl.so.3 modified unknown 0.75 .rodata, .text
Summary: 156 binaries analyzed, 3 modified, 153 unchanged
Added: 0, Removed: 0
Verdicts: unknown: 3, vanilla: 153
```
### JSON Output
@@ -65,12 +67,13 @@ 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...)
Base: docker://registry.example.com/myapp:1.0.0
Target: docker://registry.example.com/myapp:1.0.1
Platform: linux/amd64
Binaries: 156 total, 3 modified, 153 unchanged
Verdicts: 2 patched, 1 vanilla
Added: 0, Removed: 0
Verdicts: unknown: 3, vanilla: 153
```
## Using Digest References
@@ -132,9 +135,8 @@ Output includes:
| 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 |
| `vanilla` | Unchanged binary | No action required |
| `unknown` | Diff detected but classifier is not yet applied | Manual review recommended |
## Next Steps

View File

@@ -20,7 +20,6 @@ jobs:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write # For keyless signing
steps:
- name: Checkout
@@ -38,6 +37,12 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Write Signing Key
run: |
mkdir -p keys
printf '%s' "${{ secrets.BINARYDIFF_SIGNING_KEY_PEM }}" > keys/binarydiff.pem
chmod 600 keys/binarydiff.pem
- name: Get Previous Tag
id: prev-tag
run: |
@@ -52,6 +57,7 @@ jobs:
--target ghcr.io/${{ github.repository }}:${{ github.ref_name }} \
--mode=elf \
--emit-dsse=./attestations/ \
--signing-key=./keys/binarydiff.pem \
--format=json > diff.json
- name: Upload Attestations
@@ -146,11 +152,16 @@ binary-diff:
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
if [ -n "$PREV_TAG" ]; then
mkdir -p keys
printf '%s' "$BINARYDIFF_SIGNING_KEY_PEM" > keys/binarydiff.pem
chmod 600 keys/binarydiff.pem
stella scan diff \
--base ${CI_REGISTRY_IMAGE}:${PREV_TAG} \
--target ${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG} \
--mode=elf \
--emit-dsse=attestations/ \
--signing-key=keys/binarydiff.pem \
--format=json > diff.json
# Upload to GitLab artifacts
@@ -214,14 +225,18 @@ pipeline {
).trim()
if (prevTag) {
sh """
stella scan diff \\
--base ${REGISTRY}/${IMAGE}:${prevTag} \\
--target ${REGISTRY}/${IMAGE}:${TAG} \\
--mode=elf \\
--emit-dsse=attestations/ \\
--format=json > diff.json
"""
withCredentials([string(credentialsId: 'binarydiff-signing-key-pem', variable: 'BINARYDIFF_SIGNING_KEY_PEM')]) {
sh 'mkdir -p keys && printf "%s" "$BINARYDIFF_SIGNING_KEY_PEM" > keys/binarydiff.pem && chmod 600 keys/binarydiff.pem'
sh """
stella scan diff \\
--base ${REGISTRY}/${IMAGE}:${prevTag} \\
--target ${REGISTRY}/${IMAGE}:${TAG} \\
--mode=elf \\
--emit-dsse=attestations/ \\
--signing-key=keys/binarydiff.pem \\
--format=json > diff.json
"""
}
archiveArtifacts artifacts: 'attestations/*, diff.json'
@@ -272,11 +287,16 @@ steps:
script: |
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
if [ -n "$PREV_TAG" ]; then
mkdir -p $(Build.SourcesDirectory)/keys
printf '%s' "$(BINARYDIFF_SIGNING_KEY_PEM)" > $(Build.SourcesDirectory)/keys/binarydiff.pem
chmod 600 $(Build.SourcesDirectory)/keys/binarydiff.pem
stella scan diff \
--base $(REGISTRY)/$(IMAGE):${PREV_TAG} \
--target $(REGISTRY)/$(IMAGE):$(Build.SourceBranchName) \
--mode=elf \
--emit-dsse=$(Build.ArtifactStagingDirectory)/attestations/ \
--signing-key=$(Build.SourcesDirectory)/keys/binarydiff.pem \
--format=json > $(Build.ArtifactStagingDirectory)/diff.json
fi

View File

@@ -0,0 +1,44 @@
# DSSE Attestation
This example shows how to emit DSSE envelopes from `stella scan diff` and verify them.
## Generate DSSE Output
```bash
stella scan diff \
--base docker://registry.example.com/myapp:1.0.0 \
--target docker://registry.example.com/myapp:1.0.1 \
--mode=elf \
--emit-dsse=./attestations \
--signing-key=./keys/binarydiff.pem
```
Output files:
```
attestations/
linux-amd64-binarydiff.dsse.json
linux-amd64-binarydiff.payload.json
```
## Attach Attestation
```bash
stella attest attach \
--image docker://registry.example.com/myapp:1.0.1 \
--attestation ./attestations/linux-amd64-binarydiff.dsse.json
```
## Verify with Cosign
```bash
cosign verify-attestation \
--type stellaops.binarydiff.v1 \
--key ./keys/binarydiff.pub \
docker://registry.example.com/myapp:1.0.1
```
## Notes
- DSSE signing requires an ECDSA private key (P-256/384/521) in PEM format.
- If the image is multi-arch, specify `--platform` to select the manifest.

View File

@@ -0,0 +1,32 @@
# Policy Integration
Binary diff output can be used as evidence in policy decisions. This example
shows a simple workflow using the JSON output from `stella scan diff`.
## Generate JSON Output
```bash
stella scan diff \
--base myapp:1.0.0 \
--target myapp:1.0.1 \
--format=json > diff.json
```
## Feed into Policy Evaluation
Use the JSON report as an input signal for policy rules that require evidence
of binary changes. Example (pseudo-rule):
```rego
package stella.policy
allow {
input.binaryDiff.summary.modified > 0
input.binaryDiff.findings[_].changeType == "modified"
}
```
## Notes
- The CLI currently emits `unknown` verdicts for modified binaries.
- Future classifier updates will populate `patched` and `vanilla` verdicts.

View File

@@ -6,12 +6,5 @@
"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

@@ -1,27 +1,13 @@
Binary Diff: docker://registry.example.com/app:1.0.0 -> docker://registry.example.com/app:1.0.1
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
Analyzed Sections: .text, .rodata, .data, .symtab, .dynsym
Analysis Mode: ELF section hashes
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
PATH CHANGE VERDICT CONFIDENCE SECTIONS CHANGED
-------------------------- -------- ------- ---------- ----------------
/app/bin/myapp modified unknown 0.65 .rodata, .text
/usr/lib/libcrypto.so.3 modified unknown 0.70 .text
/usr/lib/libssl.so.3 modified unknown 0.75 .rodata, .text
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
Summary: 7 binaries analyzed, 3 modified, 4 unchanged
Added: 0, Removed: 0
Verdicts: unknown: 3, vanilla: 4

View File

@@ -1,179 +1,173 @@
{
"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",
"base": {
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"reference": "docker://registry.example.com/myapp:1.0.0"
},
"findings": [
{
"path": "/usr/lib/x86_64-linux-gnu/libssl.so.3",
"changeType": "modified",
"binaryFormat": "elf",
"layerDigest": "sha256:aaa111bbb222ccc333ddd444eee555fff666777888999000aaabbbcccdddeeef",
"baseHashes": {
"buildId": "abc123def456789012345678",
"fileHash": "1111111111111111111111111111111111111111111111111111111111111111",
"fileHash": "1212121212121212121212121212121212121212121212121212121212121212",
"sections": {
".text": {
"sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"size": 524288,
"offset": 4096
},
".rodata": {
"sha256": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"size": 131072,
"offset": 528384
"sha256": "3434343434343434343434343434343434343434343434343434343434343434",
"size": 4096
},
".text": {
"sha256": "5656565656565656565656565656565656565656565656565656565656565656",
"size": 65536
}
}
},
"binaryFormat": "elf",
"changeType": "modified",
"confidence": 0.65,
"layerDigest": "sha256:5555555555555555555555555555555555555555555555555555555555555555",
"path": "/app/bin/myapp",
"sectionDeltas": [
{
"baseSha256": "3434343434343434343434343434343434343434343434343434343434343434",
"section": ".rodata",
"sizeDelta": 64,
"status": "modified",
"targetSha256": "9090909090909090909090909090909090909090909090909090909090909090"
},
{
"baseSha256": "5656565656565656565656565656565656565656565656565656565656565656",
"section": ".text",
"sizeDelta": 256,
"status": "modified",
"targetSha256": "abababababababababababababababababababababababababababababababab"
}
],
"targetHashes": {
"buildId": "def789abc012345678901234",
"fileHash": "2222222222222222222222222222222222222222222222222222222222222222",
"fileHash": "7878787878787878787878787878787878787878787878787878787878787878",
"sections": {
".text": {
"sha256": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
"size": 524544,
"offset": 4096
},
".rodata": {
"sha256": "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
"size": 131200,
"offset": 528640
"sha256": "9090909090909090909090909090909090909090909090909090909090909090",
"size": 4160
},
".text": {
"sha256": "abababababababababababababababababababababababababababababababab",
"size": 65792
}
}
},
"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",
"baseHashes": {
"fileHash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"sections": {
".rodata": {
"sha256": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"size": 120000
},
".text": {
"sha256": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
"size": 600000
}
}
},
"binaryFormat": "elf",
"changeType": "modified",
"confidence": 0.7,
"layerDigest": "sha256:4444444444444444444444444444444444444444444444444444444444444444",
"path": "/usr/lib/libcrypto.so.3",
"sectionDeltas": [
{
"baseSha256": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
"section": ".text",
"sizeDelta": 512,
"status": "modified",
"sizeDelta": 2048
},
{
"section": ".rodata",
"status": "modified",
"sizeDelta": 512
},
{
"section": ".data",
"status": "modified",
"sizeDelta": 128
"targetSha256": "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
}
],
"confidence": 0.98,
"verdict": "vanilla"
"targetHashes": {
"fileHash": "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
"sections": {
".rodata": {
"sha256": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"size": 120000
},
".text": {
"sha256": "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"size": 600512
}
}
},
"verdict": "unknown"
},
{
"baseHashes": {
"fileHash": "4444444444444444444444444444444444444444444444444444444444444444",
"sections": {
".rodata": {
"sha256": "5555555555555555555555555555555555555555555555555555555555555555",
"size": 131072
},
".text": {
"sha256": "6666666666666666666666666666666666666666666666666666666666666666",
"size": 524288
}
}
},
"binaryFormat": "elf",
"changeType": "modified",
"confidence": 0.75,
"layerDigest": "sha256:3333333333333333333333333333333333333333333333333333333333333333",
"path": "/usr/lib/libssl.so.3",
"sectionDeltas": [
{
"baseSha256": "5555555555555555555555555555555555555555555555555555555555555555",
"section": ".rodata",
"sizeDelta": 128,
"status": "modified",
"targetSha256": "8888888888888888888888888888888888888888888888888888888888888888"
},
{
"baseSha256": "6666666666666666666666666666666666666666666666666666666666666666",
"section": ".text",
"sizeDelta": 256,
"status": "modified",
"targetSha256": "9999999999999999999999999999999999999999999999999999999999999999"
}
],
"targetHashes": {
"fileHash": "7777777777777777777777777777777777777777777777777777777777777777",
"sections": {
".rodata": {
"sha256": "8888888888888888888888888888888888888888888888888888888888888888",
"size": 131200
},
".text": {
"sha256": "9999999999999999999999999999999999999999999999999999999999999999",
"size": 524544
}
}
},
"verdict": "unknown"
}
],
"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
"platform": {
"architecture": "amd64",
"os": "linux"
},
"metadata": {
"toolVersion": "1.0.0",
"analysisTimestamp": "2026-01-13T12:00:00.000000Z",
"configDigest": "sha256:config123456789abcdef0123456789abcdef0123456789abcdef0123456789ab"
}
"schemaVersion": "1.0.0",
"summary": {
"added": 0,
"modified": 3,
"removed": 0,
"totalBinaries": 7,
"unchanged": 4,
"verdicts": {
"unknown": 3,
"vanilla": 4
}
},
"target": {
"digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"reference": "docker://registry.example.com/myapp:1.0.1"
},
"timestamp": "2026-01-13T12:00:00+00:00"
}