mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-07-26 11:49:16 +00:00
feat(security): add SBOM attestations, VEX document, and retire .trivyignore (#790)
* feat(security): add SBOM attestations, VEX document, and retire .trivyignore Add OpenVEX triage document (security/vex/sencho.openvex.json) for the 5 residual CVEs vendored inside docker/compose v5.1.2 that were carried over from the previous PR. All 5 are marked not_affected with justifications. Configure Trivy in both CI and release workflows to consume the VEX document via trivy.yaml so the same source of truth gates PR scans and release scans. Delete .trivyignore, which is fully superseded by the VEX file. Add two new release pipeline steps after image publication: - CycloneDX 1.6 SBOM via anchore/sbom-action (also installs syft) - SPDX 2.3 SBOM via syft directly (reuses OCI layer cache from prior step) Both are attached as cosign OCI referrer attestations (keyless, OIDC-signed) and uploaded as GitHub Release assets alongside the OpenVEX file. Bump docker-publish.yml permissions from contents:read to contents:write, required for softprops/action-gh-release to create Release assets. Add docs/operations/verifying-images.mdx with copy-paste verification commands for all supply-chain artifacts: signature, SLSA provenance, CycloneDX SBOM, SPDX SBOM, OpenVEX, and Rekor entry. Update docs.json navigation and expand the Supply chain security section in docs/security.mdx. Add a Verifying Release Artifacts section to SECURITY.md. * fix(vex): cover otel SDK CVE-2026-39883 in rebuilt Docker CLI binary The rebuilt Docker CLI v29.4.0 vendors otel/sdk v1.42.0, which still contains CVE-2026-39883 (BSD kenv PATH hijacking; fixed in v1.43.0). docker-compose v5.1.2 vendors otel/sdk v1.38.0 separately. The original VEX statement only covered the compose binary's location and version, so Trivy's scan of /usr/local/bin/docker was not suppressed. Add a second subcomponent entry for the CLI binary path with the correct vendored version. The not_affected justification (BSD-only code path; we ship linux/amd64 and linux/arm64 only) holds for both binaries. * fix(ci): use list form for vulnerability.vex in trivy.yaml Trivy's config schema requires vulnerability.vex to be a list (mapped to the multi-value --vex flag). The previous bare-string value was silently dropped, so the OpenVEX document was never loaded and HIGH findings already covered by VEX statements still failed the scan. * fix(ci): mirror VEX CVE in .trivyignore for local-image scan Trivy does not emit an OCI purl for locally-built images without a RepoDigests entry (aquasecurity/trivy#9399), so OpenVEX product matching against the CI build target sencho:pr-test resolves to no artifact and every statement is silently dropped. The VEX document remains the canonical triage record and is still attached as a cosign attestation on the published image; this file just mirrors the single CVE that surfaces on the local scan so CI does not block on a finding already triaged in VEX. Updates the Trivy step comment to document the relationship between the two files.
This commit is contained in:
@@ -152,15 +152,23 @@ jobs:
|
|||||||
APK_CACHE_BUST=${{ steps.apk-bust.outputs.date }}
|
APK_CACHE_BUST=${{ steps.apk-bust.outputs.date }}
|
||||||
|
|
||||||
- name: Scan image for vulnerabilities (Trivy)
|
- name: Scan image for vulnerabilities (Trivy)
|
||||||
# Hard-fails the PR on any HIGH or CRITICAL finding that is not listed
|
# Hard-fails the PR on any HIGH or CRITICAL finding not suppressed by
|
||||||
# in .trivyignore at the repo root. To accept a CVE, add it to that
|
# the OpenVEX document at security/vex/sencho.openvex.json (loaded via
|
||||||
# file with a human-readable justification comment, not here.
|
# trivy.yaml) or by .trivyignore at the repo root. The VEX document is
|
||||||
|
# the canonical triage record and is also attached as a cosign
|
||||||
|
# attestation on the published image. .trivyignore exists only to
|
||||||
|
# mirror VEX entries that cannot be resolved here because Trivy does
|
||||||
|
# not emit an OCI purl for local images without a digest, so VEX
|
||||||
|
# product matching against `sencho:pr-test` is a no-op (see
|
||||||
|
# aquasecurity/trivy#9399). Add the VEX statement first, then mirror
|
||||||
|
# the CVE id in .trivyignore with a pointer to the VEX justification.
|
||||||
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
|
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
|
||||||
with:
|
with:
|
||||||
image-ref: sencho:pr-test
|
image-ref: sencho:pr-test
|
||||||
exit-code: '1'
|
exit-code: '1'
|
||||||
severity: 'CRITICAL,HIGH'
|
severity: 'CRITICAL,HIGH'
|
||||||
format: 'table'
|
format: 'table'
|
||||||
|
trivy-config: trivy.yaml
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# E2E Tests (PRs only, skipped for release-please PRs)
|
# E2E Tests (PRs only, skipped for release-please PRs)
|
||||||
|
|||||||
@@ -23,8 +23,9 @@ jobs:
|
|||||||
# a manual-approval gate without any workflow change.
|
# a manual-approval gate without any workflow change.
|
||||||
environment: production
|
environment: production
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: write
|
||||||
# Required for cosign keyless signing via GitHub OIDC.
|
# Required for cosign keyless signing via GitHub OIDC and for uploading
|
||||||
|
# SBOM/VEX files to GitHub Releases via softprops/action-gh-release.
|
||||||
id-token: write
|
id-token: write
|
||||||
steps:
|
steps:
|
||||||
- name: Check out the repo
|
- name: Check out the repo
|
||||||
@@ -102,14 +103,16 @@ jobs:
|
|||||||
|
|
||||||
- name: Re-scan release image for vulnerabilities (Trivy)
|
- name: Re-scan release image for vulnerabilities (Trivy)
|
||||||
# Gates the release on the same HIGH/CRITICAL policy as the PR scan.
|
# Gates the release on the same HIGH/CRITICAL policy as the PR scan.
|
||||||
# Entries in .trivyignore at the repo root are honored so the acknowledged
|
# CVEs suppressed via the OpenVEX document (trivy.yaml -> security/vex/
|
||||||
# CVE list is the single source of truth across PR CI and release CI.
|
# sencho.openvex.json) are the single source of truth across PR CI and
|
||||||
|
# release CI.
|
||||||
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
|
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
|
||||||
with:
|
with:
|
||||||
image-ref: localhost/sencho:release-scan
|
image-ref: localhost/sencho:release-scan
|
||||||
exit-code: '1'
|
exit-code: '1'
|
||||||
severity: 'CRITICAL,HIGH'
|
severity: 'CRITICAL,HIGH'
|
||||||
format: 'table'
|
format: 'table'
|
||||||
|
trivy-config: trivy.yaml
|
||||||
|
|
||||||
# Start the scanned image headless on the runner and poll /api/health
|
# Start the scanned image headless on the runner and poll /api/health
|
||||||
# until it returns 200. Catches entrypoint regressions, native module
|
# until it returns 200. Catches entrypoint regressions, native module
|
||||||
@@ -168,6 +171,8 @@ jobs:
|
|||||||
# cosign verify saelix/sencho:<tag> \
|
# cosign verify saelix/sencho:<tag> \
|
||||||
# --certificate-identity-regexp "https://github.com/AnsoCode/Sencho/.*" \
|
# --certificate-identity-regexp "https://github.com/AnsoCode/Sencho/.*" \
|
||||||
# --certificate-oidc-issuer https://token.actions.githubusercontent.com
|
# --certificate-oidc-issuer https://token.actions.githubusercontent.com
|
||||||
|
# Each ref is pinned to the immutable digest so signatures are bound to
|
||||||
|
# the exact manifest, not the mutable tag pointer.
|
||||||
env:
|
env:
|
||||||
TAGS: ${{ steps.meta.outputs.tags }}
|
TAGS: ${{ steps.meta.outputs.tags }}
|
||||||
DIGEST: ${{ steps.build.outputs.digest }}
|
DIGEST: ${{ steps.build.outputs.digest }}
|
||||||
@@ -186,3 +191,43 @@ jobs:
|
|||||||
else
|
else
|
||||||
echo "No tags to sign (likely a workflow_dispatch run on a non-tag ref)."
|
echo "No tags to sign (likely a workflow_dispatch run on a non-tag ref)."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
- name: Generate CycloneDX SBOM
|
||||||
|
# syft scans the published manifest by digest for richer package
|
||||||
|
# extraction than the BuildKit-native SBOM. Also installs syft into
|
||||||
|
# PATH so the SPDX step below can reuse the OCI layer cache.
|
||||||
|
uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.20.2
|
||||||
|
with:
|
||||||
|
image: saelix/sencho@${{ steps.build.outputs.digest }}
|
||||||
|
format: cyclonedx-json
|
||||||
|
output-file: sbom.cdx.json
|
||||||
|
upload-artifact: false
|
||||||
|
|
||||||
|
- name: Generate SPDX SBOM
|
||||||
|
# Reuses the syft binary and OCI layer cache from the prior step.
|
||||||
|
run: |
|
||||||
|
syft saelix/sencho@${{ steps.build.outputs.digest }} \
|
||||||
|
-o spdx-json=sbom.spdx.json
|
||||||
|
|
||||||
|
- name: Attest SBOMs and VEX with cosign (keyless)
|
||||||
|
# Attaches CycloneDX SBOM, SPDX SBOM, and OpenVEX document as signed
|
||||||
|
# OCI referrer attestations on the published digest. Verification
|
||||||
|
# commands are documented in docs/operations/verifying-images.mdx.
|
||||||
|
env:
|
||||||
|
DIGEST: ${{ steps.build.outputs.digest }}
|
||||||
|
run: |
|
||||||
|
IMAGE_REF="saelix/sencho@${DIGEST}"
|
||||||
|
cosign attest --yes --predicate sbom.cdx.json --type cyclonedx "${IMAGE_REF}"
|
||||||
|
cosign attest --yes --predicate sbom.spdx.json --type spdxjson "${IMAGE_REF}"
|
||||||
|
cosign attest --yes --predicate security/vex/sencho.openvex.json --type openvex "${IMAGE_REF}"
|
||||||
|
|
||||||
|
- name: Upload SBOM and VEX to GitHub Release
|
||||||
|
# Fallback for consumers who do not use cosign; files are also
|
||||||
|
# available as signed OCI attestations via the attest step above.
|
||||||
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
|
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2.3.2
|
||||||
|
with:
|
||||||
|
files: |
|
||||||
|
sbom.cdx.json
|
||||||
|
sbom.spdx.json
|
||||||
|
security/vex/sencho.openvex.json
|
||||||
|
|||||||
+20
-70
@@ -1,75 +1,25 @@
|
|||||||
# Trivy ignore list
|
# Trivy ignore list (CI build-and-scan only)
|
||||||
#
|
#
|
||||||
# Every entry in this file is a known HIGH or CRITICAL CVE that we have
|
# The OpenVEX document at security/vex/sencho.openvex.json is the canonical
|
||||||
# consciously accepted risk on and decided not to block CI over. Format:
|
# source of truth for accepted CVEs. It is consumed by Trivy via trivy.yaml
|
||||||
|
# and attached as a cosign attestation on every published image, so any
|
||||||
|
# downstream consumer who scans saelix/sencho:<tag> sees the same triage.
|
||||||
#
|
#
|
||||||
# CVE-YYYY-NNNNN
|
# This file exists only because the OpenVEX product purl matching does not
|
||||||
# # Justification: why we're accepting this risk, and a link or note about
|
# resolve for locally-built images that have no RepoDigests. Trivy does not
|
||||||
# # when to revisit (e.g. "blocked on upstream base image update, revisit
|
# generate an OCI purl for such images (see aquasecurity/trivy#9399), so the
|
||||||
# # when alpine/node:22 ships a fix").
|
# product `pkg:oci/sencho` in our VEX statements never matches the artifact
|
||||||
|
# under test in the PR-time scan against `sencho:pr-test`. Once the image is
|
||||||
|
# pushed and acquires a digest, VEX matching resolves and this file is no
|
||||||
|
# longer load-bearing for that scan.
|
||||||
#
|
#
|
||||||
# Rules:
|
# Rule: every entry here MUST already have a corresponding statement in
|
||||||
# - Every CVE MUST have a justification comment directly above it.
|
# security/vex/sencho.openvex.json with a justification. Do not add a CVE
|
||||||
# - If there is no justification, the CVE is not ignored - add it here only
|
# here without first adding the VEX statement; the VEX file is what users
|
||||||
# after a human review and a decision to accept the risk.
|
# audit, not this one.
|
||||||
# - Review this file on every release; remove entries whose upstream fix has
|
|
||||||
# landed.
|
|
||||||
#
|
|
||||||
# Picked up automatically by aquasecurity/trivy-action from the repo root
|
|
||||||
# working directory. Both the pre-push PR scan (.github/workflows/ci.yml) and
|
|
||||||
# the release-time re-scan (.github/workflows/docker-publish.yml) honor it.
|
|
||||||
#
|
|
||||||
# Note: The following CVEs that were previously accepted here have been resolved
|
|
||||||
# by building Docker CLI and Compose from source against Go 1.26.2 (PR feat/security-pin-and-rebuild):
|
|
||||||
# CVE-2026-32280, CVE-2026-32281, CVE-2026-32282, CVE-2026-32283 (Go stdlib x509/TLS, fixed in Go 1.26.2)
|
|
||||||
# CVE-2026-33810 (Go stdlib DNS name constraint bypass, fixed in Go 1.26.2)
|
|
||||||
# CVE-2026-33186 (grpc 1.78.0 HTTP/2 server attack, eliminated by source rebuild)
|
|
||||||
# CVE-2026-33671 (picomatch ReDoS in npm, eliminated by removing npm from runtime image)
|
|
||||||
#
|
|
||||||
# The remaining entries below are vendored inside docker/compose v5.1.2 and
|
|
||||||
# cannot be patched without a new upstream compose release. They will be
|
|
||||||
# migrated to security/vex/sencho.openvex.json (OpenVEX format) in the
|
|
||||||
# follow-up PR (feat/security-sbom-vex), after which this file will be deleted.
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# Justification: covered by VEX statement for CVE-2026-39883 (otel/sdk
|
||||||
# Bundled inside /usr/local/lib/docker/cli-plugins/docker-compose (v5.1.2)
|
# BSD kenv PATH hijacking). The vulnerable code path executes only on
|
||||||
# ---------------------------------------------------------------------------
|
# BSD operating systems and Sencho ships linux/amd64 + linux/arm64 only.
|
||||||
# Compose v5.1.2 is the latest upstream release. It statically links older
|
# See security/vex/sencho.openvex.json for the full impact statement.
|
||||||
# copies of github.com/docker/docker, buildkit, and otel. We cannot bump
|
|
||||||
# these transitively without waiting for a new upstream Compose release.
|
|
||||||
# Revisit this block on every Compose release; remove entries as upstream
|
|
||||||
# rebuilds ship the fixes.
|
|
||||||
|
|
||||||
# Justification: github.com/docker/docker v28.5.2 statically bundled in
|
|
||||||
# compose v5.1.2. Moby authz bypass applies to a Docker daemon, not to the
|
|
||||||
# compose CLI plugin; compose never runs as a daemon. Revisit on next
|
|
||||||
# Compose upstream release.
|
|
||||||
CVE-2026-34040
|
|
||||||
|
|
||||||
# Justification: github.com/moby/buildkit v0.27.1 statically bundled in
|
|
||||||
# compose v5.1.2. BuildKit arbitrary file write via untrusted frontend is
|
|
||||||
# exploited at buildkit build time with attacker-controlled frontends; our
|
|
||||||
# compose invocations only call up/down/ps against local user-authored
|
|
||||||
# compose files, never as a build frontend. Revisit on next Compose upstream
|
|
||||||
# release.
|
|
||||||
CVE-2026-33747
|
|
||||||
|
|
||||||
# Justification: github.com/moby/buildkit v0.27.1 statically bundled in
|
|
||||||
# compose v5.1.2. Same exposure profile as CVE-2026-33747 (Git URL fragment
|
|
||||||
# subdir exploitation requires invoking buildkit on untrusted repo URLs,
|
|
||||||
# which compose does not do in our flow). Revisit on next Compose upstream
|
|
||||||
# release.
|
|
||||||
CVE-2026-33748
|
|
||||||
|
|
||||||
# Justification: go.opentelemetry.io/otel/sdk v1.38.0 statically bundled in
|
|
||||||
# compose v5.1.2. PATH hijacking requires the attacker to control the
|
|
||||||
# process PATH before compose starts; our container starts compose from a
|
|
||||||
# fixed PATH with only /usr/local/bin and /usr/bin on it, both owned by
|
|
||||||
# root. Revisit on next Compose upstream release.
|
|
||||||
CVE-2026-24051
|
|
||||||
|
|
||||||
# Justification: go.opentelemetry.io/otel/sdk v1.38.0 statically bundled in
|
|
||||||
# compose v5.1.2. BSD kenv PATH hijacking only applies on BSD systems; we
|
|
||||||
# ship linux/amd64 and linux/arm64. Not applicable in our runtime. Revisit
|
|
||||||
# on next Compose upstream release.
|
|
||||||
CVE-2026-39883
|
CVE-2026-39883
|
||||||
|
|||||||
+18
@@ -28,3 +28,21 @@ Sencho manages Docker containers and has access to the Docker socket. When deplo
|
|||||||
- Use strong passwords and rotate JWT secrets
|
- Use strong passwords and rotate JWT secrets
|
||||||
- Restrict network access to the Sencho port
|
- Restrict network access to the Sencho port
|
||||||
- Review the [security configuration docs](https://docs.sencho.io) for hardening guidance
|
- Review the [security configuration docs](https://docs.sencho.io) for hardening guidance
|
||||||
|
|
||||||
|
## Verifying Release Artifacts
|
||||||
|
|
||||||
|
Every published image is signed and carries verifiable supply-chain artifacts. See the full guide at [docs.sencho.io/operations/verifying-images](https://docs.sencho.io/operations/verifying-images).
|
||||||
|
|
||||||
|
Quick summary:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Verify image signature (cosign keyless, Rekor logged)
|
||||||
|
cosign verify saelix/sencho:<tag> \
|
||||||
|
--certificate-identity-regexp "https://github.com/AnsoCode/Sencho/.*" \
|
||||||
|
--certificate-oidc-issuer https://token.actions.githubusercontent.com
|
||||||
|
|
||||||
|
# Scan with VEX applied (zero unresolved HIGH/CRITICAL CVEs)
|
||||||
|
trivy image --vex sencho.openvex.json --severity HIGH,CRITICAL saelix/sencho:<tag>
|
||||||
|
```
|
||||||
|
|
||||||
|
CycloneDX SBOM (`sbom.cdx.json`), SPDX SBOM (`sbom.spdx.json`), and the VEX document (`sencho.openvex.json`) are attached to every GitHub Release as downloadable assets and as cosign attestations on the image digest.
|
||||||
|
|||||||
+2
-2
@@ -138,7 +138,6 @@
|
|||||||
"security",
|
"security",
|
||||||
"features/node-compatibility",
|
"features/node-compatibility",
|
||||||
"reference/settings",
|
"reference/settings",
|
||||||
"reference/verifying-images",
|
|
||||||
"reference/contact",
|
"reference/contact",
|
||||||
"reference/security-advisories"
|
"reference/security-advisories"
|
||||||
]
|
]
|
||||||
@@ -151,7 +150,8 @@
|
|||||||
"operations/upgrade",
|
"operations/upgrade",
|
||||||
"operations/self-hosting",
|
"operations/self-hosting",
|
||||||
"operations/trivy-setup",
|
"operations/trivy-setup",
|
||||||
"operations/two-factor-admin"
|
"operations/two-factor-admin",
|
||||||
|
"operations/verifying-images"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
---
|
||||||
|
title: Verifying Published Images
|
||||||
|
description: How to verify signatures, provenance, SBOMs, and CVE triage for Sencho Docker images.
|
||||||
|
---
|
||||||
|
|
||||||
|
Every Sencho release image published to Docker Hub is signed and carries verifiable supply-chain artifacts. This page explains how to check each one.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Install the following tools once:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# cosign (Sigstore)
|
||||||
|
brew install cosign # macOS
|
||||||
|
# or: https://github.com/sigstore/cosign/releases
|
||||||
|
|
||||||
|
# syft (optional, for inspecting SBOM content)
|
||||||
|
brew install syft
|
||||||
|
# or: https://github.com/anchore/syft/releases
|
||||||
|
|
||||||
|
# Trivy (optional, for local vulnerability scan with VEX)
|
||||||
|
brew install trivy
|
||||||
|
# or: https://github.com/aquasecurity/trivy/releases
|
||||||
|
```
|
||||||
|
|
||||||
|
## Verify the image signature
|
||||||
|
|
||||||
|
Every published tag is signed with cosign keyless signing via GitHub Actions OIDC. No private key is stored anywhere; the signing identity is bound to the GitHub Actions workflow that published the image.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cosign verify saelix/sencho:<tag> \
|
||||||
|
--certificate-identity-regexp "https://github.com/AnsoCode/Sencho/.*" \
|
||||||
|
--certificate-oidc-issuer https://token.actions.githubusercontent.com
|
||||||
|
```
|
||||||
|
|
||||||
|
A successful verification prints the verified payload and exits 0. The Rekor transparency log entry is included in the output; you can also query it directly:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cosign tree saelix/sencho:<tag>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Verify SLSA provenance
|
||||||
|
|
||||||
|
BuildKit produces an SLSA v1 provenance attestation during every multi-arch build. Retrieve and verify it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cosign verify-attestation \
|
||||||
|
--type slsaprovenance \
|
||||||
|
--certificate-identity-regexp "https://github.com/AnsoCode/Sencho/.*" \
|
||||||
|
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
|
||||||
|
saelix/sencho:<tag> | jq -r '.payload' | base64 -d | jq
|
||||||
|
```
|
||||||
|
|
||||||
|
## Retrieve the CycloneDX SBOM
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cosign verify-attestation \
|
||||||
|
--type cyclonedx \
|
||||||
|
--certificate-identity-regexp "https://github.com/AnsoCode/Sencho/.*" \
|
||||||
|
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
|
||||||
|
saelix/sencho:<tag> | jq -r '.payload' | base64 -d | jq
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, download `sbom.cdx.json` directly from the GitHub Release assets for the matching version.
|
||||||
|
|
||||||
|
## Retrieve the SPDX SBOM
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cosign verify-attestation \
|
||||||
|
--type spdxjson \
|
||||||
|
--certificate-identity-regexp "https://github.com/AnsoCode/Sencho/.*" \
|
||||||
|
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
|
||||||
|
saelix/sencho:<tag> | jq -r '.payload' | base64 -d | jq
|
||||||
|
```
|
||||||
|
|
||||||
|
`sbom.spdx.json` is also available as a GitHub Release asset.
|
||||||
|
|
||||||
|
## Retrieve the VEX document
|
||||||
|
|
||||||
|
The OpenVEX document explains Sencho's triage decisions for any CVEs that appear in vendored dependencies but are not reachable via Sencho's usage of those dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cosign verify-attestation \
|
||||||
|
--type openvex \
|
||||||
|
--certificate-identity-regexp "https://github.com/AnsoCode/Sencho/.*" \
|
||||||
|
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
|
||||||
|
saelix/sencho:<tag> | jq -r '.payload' | base64 -d | jq
|
||||||
|
```
|
||||||
|
|
||||||
|
`sencho.openvex.json` is also available as a GitHub Release asset and at `security/vex/sencho.openvex.json` in the source repository.
|
||||||
|
|
||||||
|
## Run a local vulnerability scan with VEX applied
|
||||||
|
|
||||||
|
To reproduce the same scan that gates every release:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Download the VEX document
|
||||||
|
curl -Lo sencho.openvex.json \
|
||||||
|
https://github.com/AnsoCode/Sencho/releases/latest/download/sencho.openvex.json
|
||||||
|
|
||||||
|
# Scan with VEX applied (exit 1 if any unresolved HIGH/CRITICAL CVE)
|
||||||
|
trivy image \
|
||||||
|
--vex sencho.openvex.json \
|
||||||
|
--severity HIGH,CRITICAL \
|
||||||
|
--exit-code 1 \
|
||||||
|
saelix/sencho:<tag>
|
||||||
|
```
|
||||||
|
|
||||||
|
A clean result means no unresolved HIGH or CRITICAL CVEs. CVEs listed as `not_affected` in the VEX document are suppressed with their justification visible in the VEX file.
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
Trivy 0.36.0 or later is required for `--vex` flag support.
|
||||||
|
</Note>
|
||||||
+9
-3
@@ -233,9 +233,15 @@ For multi-node setup, see [Multi-Node Management](/features/multi-node).
|
|||||||
|
|
||||||
## Supply chain security
|
## Supply chain security
|
||||||
|
|
||||||
Sencho Docker images are signed with [Sigstore cosign](https://www.sigstore.dev/) and include SBOM (Software Bill of Materials) and SLSA provenance attestations. Dependency versions are pinned in the Dockerfile for reproducible builds.
|
Every Sencho release image ships a set of verifiable supply-chain artifacts:
|
||||||
|
|
||||||
For verification instructions, see [Verifying Images](/reference/verifying-images). For CVE remediation history, see [Security Advisories](/reference/security-advisories).
|
- **Cosign signature** (keyless, GitHub OIDC): every tag is signed by digest and recorded in the Sigstore Rekor transparency log. No private key is stored anywhere.
|
||||||
|
- **SLSA provenance attestation**: produced by BuildKit at build time, describes the exact source commit, build inputs, and build steps.
|
||||||
|
- **CycloneDX 1.6 SBOM**: full package inventory in CycloneDX format, attached as a cosign attestation and downloadable as a GitHub Release asset (`sbom.cdx.json`).
|
||||||
|
- **SPDX 2.3 SBOM**: same inventory in SPDX format (`sbom.spdx.json`).
|
||||||
|
- **OpenVEX document**: triage decisions for CVEs that appear in vendored third-party dependencies but are not reachable via Sencho's usage of those dependencies (`sencho.openvex.json`).
|
||||||
|
|
||||||
|
For copy-paste verification commands, see [Verifying Images](/operations/verifying-images).
|
||||||
|
|
||||||
## Production hardening checklist
|
## Production hardening checklist
|
||||||
|
|
||||||
@@ -248,7 +254,7 @@ For verification instructions, see [Verifying Images](/reference/verifying-image
|
|||||||
3. **Enroll all admin accounts in 2FA** to protect against credential theft. See [Two-Factor Authentication](/features/two-factor-authentication).
|
3. **Enroll all admin accounts in 2FA** to protect against credential theft. See [Two-Factor Authentication](/features/two-factor-authentication).
|
||||||
4. **Use scoped API tokens** instead of sharing admin credentials with CI/CD pipelines. See [API Tokens](/features/api-tokens).
|
4. **Use scoped API tokens** instead of sharing admin credentials with CI/CD pipelines. See [API Tokens](/features/api-tokens).
|
||||||
5. **Set `FRONTEND_URL`** to restrict CORS to your domain. See [Configuration](/getting-started/configuration).
|
5. **Set `FRONTEND_URL`** to restrict CORS to your domain. See [Configuration](/getting-started/configuration).
|
||||||
6. **Verify Docker image signatures** before deploying to confirm the image has not been tampered with. See [Verifying Images](/reference/verifying-images).
|
6. **Verify Docker image signatures** before deploying to confirm the image has not been tampered with. See [Verifying Images](/operations/verifying-images).
|
||||||
7. **Review the audit log regularly** or assign a dedicated Auditor-role user for ongoing compliance monitoring. See [Audit Log](/features/audit-log).
|
7. **Review the audit log regularly** or assign a dedicated Auditor-role user for ongoing compliance monitoring. See [Audit Log](/features/audit-log).
|
||||||
8. **Keep Sencho updated** to receive security patches and dependency upgrades. See [Upgrade](/operations/upgrade).
|
8. **Keep Sencho updated** to receive security patches and dependency upgrades. See [Upgrade](/operations/upgrade).
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,120 @@
|
|||||||
|
{
|
||||||
|
"@context": "https://openvex.dev/ns/v0.2.0",
|
||||||
|
"@id": "https://github.com/AnsoCode/Sencho/security/vex/sencho.openvex.json",
|
||||||
|
"author": "Studio Saelix",
|
||||||
|
"role": "Vendor",
|
||||||
|
"timestamp": "2026-04-26T00:00:00Z",
|
||||||
|
"last_updated": "2026-04-26T00:00:00Z",
|
||||||
|
"version": 1,
|
||||||
|
"statements": [
|
||||||
|
{
|
||||||
|
"vulnerability": {
|
||||||
|
"@id": "https://www.cve.org/CVERecord?id=CVE-2026-34040",
|
||||||
|
"name": "CVE-2026-34040",
|
||||||
|
"description": "Moby/Docker daemon authorization bypass"
|
||||||
|
},
|
||||||
|
"products": [
|
||||||
|
{
|
||||||
|
"@id": "pkg:oci/sencho",
|
||||||
|
"subcomponents": [
|
||||||
|
{
|
||||||
|
"@id": "pkg:golang/github.com/docker/docker@v28.5.2",
|
||||||
|
"location": "/usr/local/lib/docker/cli-plugins/docker-compose"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"status": "not_affected",
|
||||||
|
"justification": "vulnerable_code_not_in_execute_path",
|
||||||
|
"impact_statement": "The authz bypass applies to Docker daemon plugin authorization hooks. docker-compose v5.1.2 statically bundles docker/docker v28.5.2 as a library but never acts as a daemon and never runs authorization hooks. Sencho invokes compose only for up/down/ps operations against local user-authored compose files. The daemon authz code path is not reachable."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"vulnerability": {
|
||||||
|
"@id": "https://www.cve.org/CVERecord?id=CVE-2026-33747",
|
||||||
|
"name": "CVE-2026-33747",
|
||||||
|
"description": "BuildKit arbitrary file write via untrusted frontend"
|
||||||
|
},
|
||||||
|
"products": [
|
||||||
|
{
|
||||||
|
"@id": "pkg:oci/sencho",
|
||||||
|
"subcomponents": [
|
||||||
|
{
|
||||||
|
"@id": "pkg:golang/github.com/moby/buildkit@v0.27.1",
|
||||||
|
"location": "/usr/local/lib/docker/cli-plugins/docker-compose"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"status": "not_affected",
|
||||||
|
"justification": "vulnerable_code_not_in_execute_path",
|
||||||
|
"impact_statement": "docker-compose v5.1.2 statically bundles buildkit v0.27.1. The arbitrary file write vulnerability is exploitable only when buildkit processes an attacker-controlled frontend (e.g. a malicious Dockerfile image reference). Sencho invokes compose only for up/down/ps operations against user-authored compose files. Compose does not invoke the buildkit frontend execution path in this usage."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"vulnerability": {
|
||||||
|
"@id": "https://www.cve.org/CVERecord?id=CVE-2026-33748",
|
||||||
|
"name": "CVE-2026-33748",
|
||||||
|
"description": "BuildKit Git URL fragment subdir path traversal"
|
||||||
|
},
|
||||||
|
"products": [
|
||||||
|
{
|
||||||
|
"@id": "pkg:oci/sencho",
|
||||||
|
"subcomponents": [
|
||||||
|
{
|
||||||
|
"@id": "pkg:golang/github.com/moby/buildkit@v0.27.1",
|
||||||
|
"location": "/usr/local/lib/docker/cli-plugins/docker-compose"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"status": "not_affected",
|
||||||
|
"justification": "vulnerable_code_not_in_execute_path",
|
||||||
|
"impact_statement": "The Git URL fragment subdir traversal is exploitable only when buildkit fetches an attacker-controlled Git repository URL as a build context. Sencho invokes compose for up/down/ps against local user-authored compose files only. Compose does not pass Git URLs to buildkit in this usage."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"vulnerability": {
|
||||||
|
"@id": "https://www.cve.org/CVERecord?id=CVE-2026-24051",
|
||||||
|
"name": "CVE-2026-24051",
|
||||||
|
"description": "OpenTelemetry SDK PATH hijacking on process start"
|
||||||
|
},
|
||||||
|
"products": [
|
||||||
|
{
|
||||||
|
"@id": "pkg:oci/sencho",
|
||||||
|
"subcomponents": [
|
||||||
|
{
|
||||||
|
"@id": "pkg:golang/go.opentelemetry.io/otel/sdk@v1.38.0",
|
||||||
|
"location": "/usr/local/lib/docker/cli-plugins/docker-compose"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"status": "not_affected",
|
||||||
|
"justification": "inline_mitigations_already_exist",
|
||||||
|
"impact_statement": "The PATH hijacking requires an attacker to control the process PATH environment variable before compose starts. The container's PATH is set to /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin at image build time. Both directories are owned by root with mode 755. Unprivileged processes cannot inject malicious binaries into these directories."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"vulnerability": {
|
||||||
|
"@id": "https://www.cve.org/CVERecord?id=CVE-2026-39883",
|
||||||
|
"name": "CVE-2026-39883",
|
||||||
|
"description": "OpenTelemetry SDK BSD kenv PATH hijacking"
|
||||||
|
},
|
||||||
|
"products": [
|
||||||
|
{
|
||||||
|
"@id": "pkg:oci/sencho",
|
||||||
|
"subcomponents": [
|
||||||
|
{
|
||||||
|
"@id": "pkg:golang/go.opentelemetry.io/otel/sdk@v1.38.0",
|
||||||
|
"location": "/usr/local/lib/docker/cli-plugins/docker-compose"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@id": "pkg:golang/go.opentelemetry.io/otel/sdk@v1.42.0",
|
||||||
|
"location": "/usr/local/bin/docker"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"status": "not_affected",
|
||||||
|
"justification": "vulnerable_code_not_present",
|
||||||
|
"impact_statement": "The BSD kenv PATH hijacking code path executes only on BSD operating systems (FreeBSD, OpenBSD, NetBSD). Sencho publishes linux/amd64 and linux/arm64 images only. The vulnerable code path is never executed in our runtime environment. Both the docker-compose plugin (otel/sdk v1.38.0) and the rebuilt Docker CLI binary (otel/sdk v1.42.0) bundle vulnerable versions; the same justification applies to both."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
vulnerability:
|
||||||
|
vex:
|
||||||
|
- security/vex/sencho.openvex.json
|
||||||
Reference in New Issue
Block a user