From 22f11723b245c47d3867e56e7a4edaf09b2db1ca Mon Sep 17 00:00:00 2001 From: chen21019 Date: Thu, 10 Sep 2026 00:24:52 +0800 Subject: [PATCH 1/3] security: refresh runtime base packages --- .github/workflows/release.yml | 8 +++++++- .github/workflows/supply-chain.yml | 8 +++++--- Dockerfile.dapper | 1 + README.md | 8 ++++---- package/Dockerfile | 1 + scripts/check-build-downloads | 3 +++ scripts/check-migration-policy | 2 +- 7 files changed, 22 insertions(+), 9 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 52e547c..0b11bc9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -158,9 +158,15 @@ jobs: run: | set +e docker logout ghcr.io >/dev/null 2>&1 + owner="$(id -u):$(id -g)" + cache="$RUNNER_TEMP/resource-scheduler-release-trivy-cache" + if [ -d "$cache" ] && docker image inspect "$TRIVY_IMAGE" >/dev/null 2>&1; then + docker run --rm --volume "$cache:/target" --entrypoint chown \ + "$TRIVY_IMAGE" -R "$owner" /target >/dev/null 2>&1 + fi for image in "${RUNTIME_IMAGE:-}" "$DAPPER_IMAGE" "$TRIVY_IMAGE"; do if [ -n "$image" ] && docker image inspect "$image" >/dev/null 2>&1; then docker image rm -f "$image" >/dev/null 2>&1 fi done - rm -rf -- bin dist "$RUNNER_TEMP/resource-scheduler-release-trivy-cache" + rm -rf -- bin dist "$cache" diff --git a/.github/workflows/supply-chain.yml b/.github/workflows/supply-chain.yml index 09733a1..4fb0de4 100644 --- a/.github/workflows/supply-chain.yml +++ b/.github/workflows/supply-chain.yml @@ -22,7 +22,7 @@ jobs: runs-on: ubuntu-24.04 timeout-minutes: 90 env: - CANDIDATE_VERSION: v0.8.17 + CANDIDATE_VERSION: v0.8.18 DAPPER_IMAGE: local/resource-scheduler-dapper:${{ github.sha }} TRIVY_IMAGE: aquasec/trivy:0.74.0@sha256:62b1e65e8869bc4b4c6aa4fa2b21595256c7c2f6018a9d9ad61caf87187c1969 @@ -248,6 +248,7 @@ jobs: runtime_secrets="$(jq '[.Results[]?.Secrets[]?] | length' evidence/runtime-security.json)" runtime_critical="$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "CRITICAL")] | length' evidence/runtime-security.json)" runtime_high="$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "HIGH")] | length' evidence/runtime-security.json)" + runtime_vulnerabilities="$(jq '[.Results[]?.Vulnerabilities[]?] | length' evidence/runtime-security.json)" dapper_secrets="$(jq '[.Results[]?.Secrets[]?] | length' evidence/dapper-security.json)" dapper_unreviewed="$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "CRITICAL" or .Severity == "HIGH") | @@ -285,11 +286,12 @@ jobs: } ] }' > evidence/dapper.openvex.json - printf 'source_secrets=%s\nruntime_secrets=%s\nruntime_critical=%s\nruntime_high=%s\ndapper_secrets=%s\ndapper_unreviewed=%s\n' \ - "$source_secrets" "$runtime_secrets" "$runtime_critical" "$runtime_high" \ + printf 'source_secrets=%s\nruntime_secrets=%s\nruntime_vulnerabilities=%s\nruntime_critical=%s\nruntime_high=%s\ndapper_secrets=%s\ndapper_unreviewed=%s\n' \ + "$source_secrets" "$runtime_secrets" "$runtime_vulnerabilities" "$runtime_critical" "$runtime_high" \ "$dapper_secrets" "$dapper_unreviewed" | tee evidence/security-summary.txt test "$source_secrets" -eq 0 test "$runtime_secrets" -eq 0 + test "$runtime_vulnerabilities" -eq 0 test "$runtime_critical" -eq 0 test "$runtime_high" -eq 0 test "$dapper_secrets" -eq 0 diff --git a/Dockerfile.dapper b/Dockerfile.dapper index eff9a2a..a477b4c 100644 --- a/Dockerfile.dapper +++ b/Dockerfile.dapper @@ -48,6 +48,7 @@ RUN set -eux; \ "${snapshot_uri}" > /etc/apt/sources.list.d/pasturestack-snapshot.sources; \ printf 'Acquire::Retries "5";\nAcquire::http::Timeout "30";\nAcquire::https::Timeout "30";\nAcquire::https::CaInfo "/etc/ssl/certs/ca-certificates.crt";\nAcquire::https::Verify-Peer "true";\nAcquire::https::Verify-Host "true";\nAcquire::http::Pipeline-Depth "0";\nAcquire::AllowInsecureRepositories "false";\nAPT::Get::AllowUnauthenticated "false";\n' > /etc/apt/apt.conf.d/80pasturestack-retries; \ apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get upgrade -y --no-install-recommends && \ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ bash="${UBUNTU_APT_BASH_VERSION}" \ ca-certificates="${UBUNTU_APT_CA_CERTIFICATES_VERSION}" \ diff --git a/README.md b/README.md index 70bc4ce..9738ab5 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ PastureStack is an independent community effort to preserve, audit, and moderniz **Upstream:** [`rancher/scheduler`](https://github.com/rancher/scheduler). This GitHub fork retains the upstream Git history, authorship, dates, and license notices. PastureStack maintenance is consolidated into one commit after the preserved upstream boundary. -The current public release and Catalog image are `v0.8.17`. +The current release candidate and next Catalog image are `v0.8.18`. ## Runtime image @@ -24,9 +24,9 @@ The native command is `resource-scheduler`. A `scheduler` executable symlink rem ## Build and test -The reviewed build uses Go 1.27.0 to compile Docker CLI 29.7.2 and Docker Buildx 0.36.1 from checksum-pinned source. A small checksum-locked Buildx patch removes its sole compiled dependency on the legacy Docker module; the two security-sensitive Buildx modules are then pinned to `github.com/moby/go-archive` 0.3.0 and `golang.org/x/mod` 0.40.0, with the final binary metadata checked in CI. The Ubuntu base image is digest-pinned; direct packages are version-pinned in `ubuntu-apt.lock` against the fixed `20260909T000000Z` Canonical snapshot, and each built image records the complete resolved `dpkg` inventory. BuildKit receives the source commit time through `SOURCE_DATE_EPOCH`, and CI rejects differing binary hashes or image IDs across clean rebuilds. +The reviewed build uses Go 1.27.0 to compile Docker CLI 29.7.2 and Docker Buildx 0.36.1 from checksum-pinned source. A small checksum-locked Buildx patch removes its sole compiled dependency on the legacy Docker module; the two security-sensitive Buildx modules are then pinned to `github.com/moby/go-archive` 0.3.0 and `golang.org/x/mod` 0.40.0, with the final binary metadata checked in CI. The Ubuntu base image is digest-pinned; all inherited base packages are updated from the fixed `20260909T000000Z` Canonical snapshot before the direct packages pinned in `ubuntu-apt.lock` are installed, and each built image records the complete resolved `dpkg` inventory. BuildKit receives the source commit time through `SOURCE_DATE_EPOCH`, and CI rejects differing binary hashes or image IDs across clean rebuilds. -The dependency graph is declared in `go.mod`, checksum-bound by `go.sum`, and committed in the standard module-aware `vendor` tree for reproducible offline builds. Security CI produces short-lived source and runtime CycloneDX SBOMs, runs binary reachability analysis, and blocks runtime Critical or High vulnerabilities and detected secrets. +The dependency graph is declared in `go.mod`, checksum-bound by `go.sum`, and committed in the standard module-aware `vendor` tree for reproducible offline builds. Security CI produces short-lived source and runtime CycloneDX SBOMs, runs binary reachability analysis, and blocks every scanner-reported runtime vulnerability and detected secret. ```bash make test @@ -34,7 +34,7 @@ make validate bash scripts/check-build-downloads go list -mod=vendor ./... bash scripts/check-migration-policy -VERSION_OVERRIDE=v0.8.17 IMAGE_NAMESPACE=pasturestack make package +VERSION_OVERRIDE=v0.8.18 IMAGE_NAMESPACE=pasturestack make package ``` CI validates source and dependency locks, tests and reproducible builds, and generates short-lived security evidence. Releases are published only from an annotated, pure numeric SemVer tag that resolves to the reviewed commit. diff --git a/package/Dockerfile b/package/Dockerfile index 1b26944..4a1b136 100644 --- a/package/Dockerfile +++ b/package/Dockerfile @@ -28,6 +28,7 @@ RUN set -eux; \ "${snapshot_uri}" > /etc/apt/sources.list.d/pasturestack-snapshot.sources; \ printf 'Acquire::Retries "5";\nAcquire::http::Timeout "30";\nAcquire::https::Timeout "30";\nAcquire::https::CaInfo "/etc/ssl/certs/ca-certificates.crt";\nAcquire::https::Verify-Peer "true";\nAcquire::https::Verify-Host "true";\nAcquire::http::Pipeline-Depth "0";\nAcquire::AllowInsecureRepositories "false";\nAPT::Get::AllowUnauthenticated "false";\n' > /etc/apt/apt.conf.d/80pasturestack-retries; \ apt-get update; \ + DEBIAN_FRONTEND=noninteractive apt-get upgrade -y --no-install-recommends; \ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ bash="${UBUNTU_APT_BASH_VERSION}" \ ca-certificates="${UBUNTU_APT_CA_CERTIFICATES_VERSION}" \ diff --git a/scripts/check-build-downloads b/scripts/check-build-downloads index 844aa9d..ea6688e 100755 --- a/scripts/check-build-downloads +++ b/scripts/check-build-downloads @@ -49,6 +49,7 @@ require_pattern Dockerfile.dapper "if grep -F \\$'dep\\\\tgithub.com/docker/dock require_pattern Dockerfile.dapper 'ARG UBUNTU_SNAPSHOT=20260909T000000Z' "dapper_snapshot_not_pinned" require_pattern Dockerfile.dapper 'ADD --checksum=sha256:6077d27c6b6f8b23590cb01ff877ed8c804a67a5442cc32b5a33da10d2bd0e90' "dapper_ca_bootstrap_not_pinned" require_pattern Dockerfile.dapper 'COPY ubuntu-apt\.lock /usr/share/pasturestack/manifests/ubuntu-apt\.lock' "dapper_apt_lock_not_copied" +require_pattern Dockerfile.dapper 'apt-get upgrade -y --no-install-recommends' "dapper_base_packages_not_updated" require_pattern Dockerfile.dapper 'gcc="\$\{UBUNTU_APT_GCC_VERSION\}"' "dapper_gcc_not_version_pinned" require_pattern Dockerfile.dapper 'linux-libc-dev="\$\{UBUNTU_APT_LINUX_LIBC_DEV_VERSION\}"' "dapper_linux_libc_dev_not_version_pinned" require_pattern Dockerfile.dapper 'dpkg-query -W' "dapper_resolved_package_manifest_missing" @@ -60,6 +61,7 @@ reject_pattern Dockerfile.dapper 'curl[^|]*\|[[:space:]]*tar' "pipe_to_tar_downl require_pattern package/Dockerfile 'ARG UBUNTU_IMAGE=ubuntu:26\.04@sha256:2260313b31c8c011cd2eebe728008efac1b3982be73eb71348ea2648d2c0e09b' "runtime_base_not_pinned" require_pattern package/Dockerfile 'ARG UBUNTU_SNAPSHOT=20260909T000000Z' "runtime_snapshot_not_pinned" require_pattern package/Dockerfile 'Acquire::https::CaInfo "/etc/ssl/certs/ca-certificates\.crt"' "runtime_snapshot_ca_not_enforced" +require_pattern package/Dockerfile 'apt-get upgrade -y --no-install-recommends' "runtime_base_packages_not_updated" require_pattern package/Dockerfile 'libcap2-bin="\$\{UBUNTU_APT_LIBCAP2_BIN_VERSION\}"' "runtime_packages_not_version_pinned" require_pattern package/Dockerfile 'runtime-ubuntu-packages\.tsv' "runtime_resolved_package_manifest_missing" require_pattern package/Dockerfile '/var/log/alternatives\.log /var/log/lastlog /var/log/faillog /var/cache/ldconfig/aux-cache' "runtime_nondeterministic_state_not_removed" @@ -95,6 +97,7 @@ require_pattern .github/workflows/supply-chain.yml 'actions/checkout@3d3c42e5aac require_pattern .github/workflows/supply-chain.yml 'actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a' "artifact_action_not_commit_pinned" require_pattern .github/workflows/supply-chain.yml 'aquasec/trivy:0\.74\.0@sha256:62b1e65e8869bc4b4c6aa4fa2b21595256c7c2f6018a9d9ad61caf87187c1969' "trivy_image_not_digest_pinned" require_pattern .github/workflows/supply-chain.yml 'govulncheck@v1\.7\.0' "govulncheck_not_version_pinned" +require_pattern .github/workflows/supply-chain.yml 'test "\$runtime_vulnerabilities" -eq 0' "runtime_all_severity_vulnerability_gate_missing" require_pattern .github/workflows/supply-chain.yml "stat -c '%u:%g' bin/resource-scheduler" "output_ownership_gate_missing" require_pattern .github/workflows/supply-chain.yml 'diff -u toolchain/buildx-security\.patch evidence/buildx-security\.patch' "buildx_patch_evidence_missing" require_pattern .github/workflows/supply-chain.yml '9d424e752f24ea0e34ccd80340428f067ae8109204c401c93786a2b0be6ea993 evidence/buildx-security\.patch' "buildx_patch_evidence_sha_missing" diff --git a/scripts/check-migration-policy b/scripts/check-migration-policy index d43d61e..423bb73 100755 --- a/scripts/check-migration-policy +++ b/scripts/check-migration-policy @@ -42,7 +42,7 @@ done grep -Fq "PastureStack is an independent community effort to preserve, audit, and modernize the ${legacy_title} 1.6 ecosystem. It is not affiliated with or endorsed by ${legacy_title} Labs or SUSE." README.md || fail "independence_notice_missing" grep -Fq 'ad28c1d94b7f1b1814fa6404886398b4f4094d64' ORIGIN.md || fail "upstream_boundary_missing" grep -Fq 'ghcr.io/pasturestack/resource-scheduler' README.md || fail "public_image_missing" -grep -Fq 'v0.8.17' README.md || fail "current_numeric_release_missing" +grep -Fq 'v0.8.18' README.md || fail "current_numeric_release_missing" grep -Fq "[[ \"\$RELEASE_TAG\" =~ ^v[0-9]+\\.[0-9]+\\.[0-9]+\$ ]]" .github/workflows/release.yml || fail "numeric_release_gate_missing" if git grep -I -n -E "${legacy_brand}-1\\.6-|${retired_short}" -- ':!vendor/**' ':!LICENSES/**' >/tmp/resource-scheduler-policy-matches 2>/dev/null; then From bd7163de44caec581660ed2e63a08854634858fe Mon Sep 17 00:00:00 2001 From: chen21019 Date: Thu, 10 Sep 2026 01:07:49 +0800 Subject: [PATCH 2/3] security: register vendor-unfixed runtime findings --- .github/workflows/release.yml | 16 +++++++- .github/workflows/supply-chain.yml | 12 ++++-- README.md | 2 +- RELEASE_NOTES.md | 9 +++++ SECURITY.md | 3 +- scripts/assess-runtime-vulnerabilities | 56 ++++++++++++++++++++++++++ scripts/check-build-downloads | 6 ++- scripts/check-migration-policy | 3 +- 8 files changed, 97 insertions(+), 10 deletions(-) create mode 100644 scripts/assess-runtime-vulnerabilities diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0b11bc9..47d92b6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -85,7 +85,11 @@ jobs: -v "$cache:/root/.cache/trivy" \ "$TRIVY_IMAGE" image --skip-db-update --offline-scan \ --scanners vuln,secret --severity UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL \ - --exit-code 1 "$RUNTIME_IMAGE" + --format json \ + --output "/release/resource-scheduler-${RELEASE_TAG#v}-security.json" "$RUNTIME_IMAGE" + bash scripts/assess-runtime-vulnerabilities \ + "$release_dir/resource-scheduler-${RELEASE_TAG#v}-security.json" \ + "$release_dir" "$RUNTIME_IMAGE" docker run --rm --network none \ -v /var/run/docker.sock:/var/run/docker.sock \ -v "$PWD/$release_dir:/release" \ @@ -108,7 +112,11 @@ jobs: sha256sum \ "resource-scheduler-${RELEASE_TAG#v}-linux-amd64.tar.xz" \ "resource-scheduler-${RELEASE_TAG#v}-image.cdx.json" \ + "resource-scheduler-${RELEASE_TAG#v}-security.json" \ "resource-scheduler-${RELEASE_TAG#v}-ubuntu-packages.tsv" \ + runtime-risk-summary.txt \ + runtime-vendor-unfixed.tsv \ + runtime-vendor-unfixed.openvex.json \ > SHA256SUMS ) @@ -146,10 +154,14 @@ jobs: set -euo pipefail gh release create "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" --verify-tag \ --title "Resource Scheduler ${RELEASE_TAG#v}" \ - --notes 'Maintained resource scheduler runtime published from a pure numeric version, with tests, vulnerability gate, SBOM, package inventory, checksums, and build provenance.' \ + --notes 'Maintained resource scheduler runtime published from a pure numeric version, with tests, SBOM, package inventory, full vulnerability evidence, an explicit register of vendor-unfixed Low/Medium findings, checksums, and build provenance.' \ "dist/release/resource-scheduler-${RELEASE_TAG#v}-linux-amd64.tar.xz" \ "dist/release/resource-scheduler-${RELEASE_TAG#v}-image.cdx.json" \ + "dist/release/resource-scheduler-${RELEASE_TAG#v}-security.json" \ "dist/release/resource-scheduler-${RELEASE_TAG#v}-ubuntu-packages.tsv" \ + dist/release/runtime-risk-summary.txt \ + dist/release/runtime-vendor-unfixed.tsv \ + dist/release/runtime-vendor-unfixed.openvex.json \ dist/release/SHA256SUMS - name: Clean run-owned resources diff --git a/.github/workflows/supply-chain.yml b/.github/workflows/supply-chain.yml index 4fb0de4..9d72f4d 100644 --- a/.github/workflows/supply-chain.yml +++ b/.github/workflows/supply-chain.yml @@ -53,6 +53,7 @@ jobs: scripts/build \ scripts/entry \ scripts/package \ + scripts/assess-runtime-vulnerabilities \ scripts/check-build-downloads \ scripts/check-migration-policy \ .github/workflows/release.yml \ @@ -249,6 +250,10 @@ jobs: runtime_critical="$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "CRITICAL")] | length' evidence/runtime-security.json)" runtime_high="$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "HIGH")] | length' evidence/runtime-security.json)" runtime_vulnerabilities="$(jq '[.Results[]?.Vulnerabilities[]?] | length' evidence/runtime-security.json)" + bash scripts/assess-runtime-vulnerabilities \ + evidence/runtime-security.json evidence "$RUNTIME_IMAGE" + runtime_vendor_unfixed="$(sed -n 's/^runtime_vendor_unfixed_low_medium=//p' evidence/runtime-risk-summary.txt)" + runtime_actionable="$(sed -n 's/^runtime_actionable=//p' evidence/runtime-risk-summary.txt)" dapper_secrets="$(jq '[.Results[]?.Secrets[]?] | length' evidence/dapper-security.json)" dapper_unreviewed="$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "CRITICAL" or .Severity == "HIGH") | @@ -286,12 +291,13 @@ jobs: } ] }' > evidence/dapper.openvex.json - printf 'source_secrets=%s\nruntime_secrets=%s\nruntime_vulnerabilities=%s\nruntime_critical=%s\nruntime_high=%s\ndapper_secrets=%s\ndapper_unreviewed=%s\n' \ - "$source_secrets" "$runtime_secrets" "$runtime_vulnerabilities" "$runtime_critical" "$runtime_high" \ + printf 'source_secrets=%s\nruntime_secrets=%s\nruntime_vulnerabilities=%s\nruntime_vendor_unfixed=%s\nruntime_actionable=%s\nruntime_critical=%s\nruntime_high=%s\ndapper_secrets=%s\ndapper_unreviewed=%s\n' \ + "$source_secrets" "$runtime_secrets" "$runtime_vulnerabilities" "$runtime_vendor_unfixed" "$runtime_actionable" "$runtime_critical" "$runtime_high" \ "$dapper_secrets" "$dapper_unreviewed" | tee evidence/security-summary.txt test "$source_secrets" -eq 0 test "$runtime_secrets" -eq 0 - test "$runtime_vulnerabilities" -eq 0 + test "$runtime_actionable" -eq 0 + test "$runtime_vulnerabilities" -eq "$runtime_vendor_unfixed" test "$runtime_critical" -eq 0 test "$runtime_high" -eq 0 test "$dapper_secrets" -eq 0 diff --git a/README.md b/README.md index 9738ab5..3e0395f 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ The native command is `resource-scheduler`. A `scheduler` executable symlink rem The reviewed build uses Go 1.27.0 to compile Docker CLI 29.7.2 and Docker Buildx 0.36.1 from checksum-pinned source. A small checksum-locked Buildx patch removes its sole compiled dependency on the legacy Docker module; the two security-sensitive Buildx modules are then pinned to `github.com/moby/go-archive` 0.3.0 and `golang.org/x/mod` 0.40.0, with the final binary metadata checked in CI. The Ubuntu base image is digest-pinned; all inherited base packages are updated from the fixed `20260909T000000Z` Canonical snapshot before the direct packages pinned in `ubuntu-apt.lock` are installed, and each built image records the complete resolved `dpkg` inventory. BuildKit receives the source commit time through `SOURCE_DATE_EPOCH`, and CI rejects differing binary hashes or image IDs across clean rebuilds. -The dependency graph is declared in `go.mod`, checksum-bound by `go.sum`, and committed in the standard module-aware `vendor` tree for reproducible offline builds. Security CI produces short-lived source and runtime CycloneDX SBOMs, runs binary reachability analysis, and blocks every scanner-reported runtime vulnerability and detected secret. +The dependency graph is declared in `go.mod`, checksum-bound by `go.sum`, and committed in the standard module-aware `vendor` tree for reproducible offline builds. Security CI produces short-lived source and runtime CycloneDX SBOMs, runs binary reachability analysis, and blocks detected secrets, every High/Critical runtime finding, and any finding for which Ubuntu has published a fixed package. Low/Medium findings without an available Ubuntu fix remain visible as `under_investigation` in the release risk register and OpenVEX artifact; they are not represented as resolved and are re-evaluated when the vendor, base digest, or vulnerability data changes. ```bash make test diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 3a48a51..b2ba2a0 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,14 @@ # Release Notes +## 0.8.18 + +- Update every runtime package available from the fixed Ubuntu snapshot. +- Preserve the full all-severity runtime scan and explicitly register Low/Medium + findings for which Ubuntu has not published a fixed package; keep them marked + `under_investigation` instead of weakening the scan or claiming resolution. +- Block release whenever a fixed package is available or a High/Critical + runtime finding remains. + ## 0.8.17 - Update the checksum-pinned Go, Docker CLI, Buildx, gRPC, and Ubuntu package diff --git a/SECURITY.md b/SECURITY.md index 9c44bce..6509967 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -22,7 +22,8 @@ Resource Scheduler receives scoped control-plane credentials, consumes host and - Apply the checksum-recorded Buildx patch and reject the build if the resulting binary still records the legacy Docker module. - Keep the runtime base image digest-pinned. Resolve exact direct-package versions from the dated Canonical snapshot in `ubuntu-apt.lock`, and retain the complete resolved `dpkg` inventory in each build and runtime image. - Verify that `go.mod`, `go.sum`, and `vendor/modules.txt` agree by compiling and testing with `-mod=vendor`. -- Run unit tests, race tests, `go vet`, formatting checks, build-policy checks, migration-policy checks, secret scanning, an SBOM inventory, and High/Critical vulnerability scanning before publishing. +- Run unit tests, race tests, `go vet`, formatting checks, build-policy checks, migration-policy checks, secret scanning, an SBOM inventory, and all-severity vulnerability scanning before publishing. +- Block every runtime High/Critical finding and every finding with a vendor-published fixed version. Preserve vendor-unfixed Low/Medium findings in the release scan, risk register, and OpenVEX as `under_investigation`; never label them fixed or not affected. - A High or Critical finding may be classified as not affected only when CI proves it is an unfixed `linux-libc-dev` header finding in the disposable builder, emits exact-package OpenVEX evidence that expires on 2026-09-15, and proves the package is absent from the runtime image. Any fixed or different builder finding remains blocking. - Publish a new immutable version when source or dependencies change; do not replace an existing release digest. diff --git a/scripts/assess-runtime-vulnerabilities b/scripts/assess-runtime-vulnerabilities new file mode 100644 index 0000000..c39abb9 --- /dev/null +++ b/scripts/assess-runtime-vulnerabilities @@ -0,0 +1,56 @@ +#!/bin/bash +set -euo pipefail + +report=${1:?usage: assess-runtime-vulnerabilities REPORT_JSON OUTPUT_DIR SUBJECT} +output_dir=${2:?usage: assess-runtime-vulnerabilities REPORT_JSON OUTPUT_DIR SUBJECT} +subject=${3:?usage: assess-runtime-vulnerabilities REPORT_JSON OUTPUT_DIR SUBJECT} + +mkdir -p "${output_dir}" + +total=$(jq '[.Results[]?.Vulnerabilities[]?] | length' "${report}") +pending=$(jq '[.Results[]?.Vulnerabilities[]? | + select((.Severity == "LOW" or .Severity == "MEDIUM") and ((.FixedVersion // "") == ""))] | length' "${report}") +actionable=$(jq '[.Results[]?.Vulnerabilities[]? | + select((.Severity != "LOW" and .Severity != "MEDIUM") or ((.FixedVersion // "") != ""))] | length' "${report}") + +jq -r '.Results[]?.Vulnerabilities[]? | + select((.Severity == "LOW" or .Severity == "MEDIUM") and ((.FixedVersion // "") == "")) | + [.VulnerabilityID, .PkgName, .InstalledVersion, (.FixedVersion // ""), .Severity, (.PkgIdentifier.PURL // "")] | @tsv' \ + "${report}" | LC_ALL=C sort -u > "${output_dir}/runtime-vendor-unfixed.tsv" + +jq -n \ + --slurpfile report "${report}" \ + --arg subject "${subject}" \ + --arg timestamp "$(date -u +'%Y-%m-%dT%H:%M:%SZ')" \ + '{ + "@context":"https://openvex.dev/ns/v0.2.0", + "@id":"https://github.com/PastureStack/resource-scheduler/security/openvex/runtime-vendor-unfixed", + author:"PastureStack Security", + timestamp:$timestamp, + version:1, + "x-pasturestack-policy":"Pending vendor fix; re-evaluate when Ubuntu publishes a fixed package, the base digest changes, or vulnerability intelligence changes.", + statements:[ + $report[0].Results[]?.Vulnerabilities[]? + | select((.Severity == "LOW" or .Severity == "MEDIUM") and ((.FixedVersion // "") == "")) + | { + vulnerability:{name:.VulnerabilityID}, + products:[{"@id":$subject}], + status:"under_investigation", + impact_statement:("Ubuntu has not published a fixed version for " + .PkgName + " " + .InstalledVersion + "; the finding remains registered and is not represented as resolved.") + } + ] + }' > "${output_dir}/runtime-vendor-unfixed.openvex.json" + +printf 'runtime_vulnerabilities=%s\nruntime_vendor_unfixed_low_medium=%s\nruntime_actionable=%s\n' \ + "${total}" "${pending}" "${actionable}" | tee "${output_dir}/runtime-risk-summary.txt" + +test "${total}" -eq "${pending}" +test "${actionable}" -eq 0 +test "$(jq '.statements | length' "${output_dir}/runtime-vendor-unfixed.openvex.json")" -eq "${pending}" +jq -e 'all(.statements[]; + .status == "under_investigation" and + (.products | length) == 1 and + (.impact_statement | contains("not represented as resolved")))' \ + "${output_dir}/runtime-vendor-unfixed.openvex.json" >/dev/null + +echo "RESOURCE_SCHEDULER_RUNTIME_RISK_OK total=${total} vendor_unfixed=${pending} actionable=0" diff --git a/scripts/check-build-downloads b/scripts/check-build-downloads index ea6688e..0fa1ba5 100755 --- a/scripts/check-build-downloads +++ b/scripts/check-build-downloads @@ -19,7 +19,7 @@ reject_pattern() { fi } -bash -n scripts/build scripts/check-build-downloads scripts/ci scripts/entry scripts/package scripts/test scripts/test-package-scripts scripts/validate scripts/version \ +bash -n scripts/assess-runtime-vulnerabilities scripts/build scripts/check-build-downloads scripts/ci scripts/entry scripts/package scripts/test scripts/test-package-scripts scripts/validate scripts/version \ scripts/check-migration-policy package/pasturestack-entrypoint.sh package/update-pasturestack-ca require_pattern Dockerfile.dapper 'ARG UBUNTU_IMAGE=ubuntu:26\.04@sha256:2260313b31c8c011cd2eebe728008efac1b3982be73eb71348ea2648d2c0e09b' "dapper_base_not_pinned" @@ -97,7 +97,9 @@ require_pattern .github/workflows/supply-chain.yml 'actions/checkout@3d3c42e5aac require_pattern .github/workflows/supply-chain.yml 'actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a' "artifact_action_not_commit_pinned" require_pattern .github/workflows/supply-chain.yml 'aquasec/trivy:0\.74\.0@sha256:62b1e65e8869bc4b4c6aa4fa2b21595256c7c2f6018a9d9ad61caf87187c1969' "trivy_image_not_digest_pinned" require_pattern .github/workflows/supply-chain.yml 'govulncheck@v1\.7\.0' "govulncheck_not_version_pinned" -require_pattern .github/workflows/supply-chain.yml 'test "\$runtime_vulnerabilities" -eq 0' "runtime_all_severity_vulnerability_gate_missing" +require_pattern .github/workflows/supply-chain.yml 'scripts/assess-runtime-vulnerabilities' "runtime_risk_assessment_missing" +require_pattern scripts/assess-runtime-vulnerabilities 'runtime_actionable=%s' "runtime_actionable_gate_missing" +require_pattern scripts/assess-runtime-vulnerabilities 'status:"under_investigation"' "vendor_unfixed_status_missing" require_pattern .github/workflows/supply-chain.yml "stat -c '%u:%g' bin/resource-scheduler" "output_ownership_gate_missing" require_pattern .github/workflows/supply-chain.yml 'diff -u toolchain/buildx-security\.patch evidence/buildx-security\.patch' "buildx_patch_evidence_missing" require_pattern .github/workflows/supply-chain.yml '9d424e752f24ea0e34ccd80340428f067ae8109204c401c93786a2b0be6ea993 evidence/buildx-security\.patch' "buildx_patch_evidence_sha_missing" diff --git a/scripts/check-migration-policy b/scripts/check-migration-policy index 423bb73..ada1e32 100755 --- a/scripts/check-migration-policy +++ b/scripts/check-migration-policy @@ -35,7 +35,8 @@ if git ls-files | grep -Eq "package/(${legacy_brand}-entrypoint\\.sh|update-${le fi for required in README.md ORIGIN.md COMPATIBILITY.md SECURITY.md THIRD_PARTY_NOTICES.md LICENSE \ - internal/metadata/client.go internal/metadata/types.go package/pasturestack-entrypoint.sh package/update-pasturestack-ca scripts/test-package-scripts; do + internal/metadata/client.go internal/metadata/types.go package/pasturestack-entrypoint.sh package/update-pasturestack-ca \ + scripts/assess-runtime-vulnerabilities scripts/test-package-scripts; do [ -f "${required}" ] || fail "missing_${required//\//_}" done From ab5448caefb1921d9db68915778102096776c301 Mon Sep 17 00:00:00 2001 From: chen21019 Date: Thu, 10 Sep 2026 01:13:32 +0800 Subject: [PATCH 3/3] security: keep release secret gate strict --- scripts/assess-runtime-vulnerabilities | 8 +++++--- scripts/check-build-downloads | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/assess-runtime-vulnerabilities b/scripts/assess-runtime-vulnerabilities index c39abb9..bcd0bd3 100644 --- a/scripts/assess-runtime-vulnerabilities +++ b/scripts/assess-runtime-vulnerabilities @@ -8,6 +8,7 @@ subject=${3:?usage: assess-runtime-vulnerabilities REPORT_JSON OUTPUT_DIR SUBJEC mkdir -p "${output_dir}" total=$(jq '[.Results[]?.Vulnerabilities[]?] | length' "${report}") +secrets=$(jq '[.Results[]?.Secrets[]?] | length' "${report}") pending=$(jq '[.Results[]?.Vulnerabilities[]? | select((.Severity == "LOW" or .Severity == "MEDIUM") and ((.FixedVersion // "") == ""))] | length' "${report}") actionable=$(jq '[.Results[]?.Vulnerabilities[]? | @@ -41,9 +42,10 @@ jq -n \ ] }' > "${output_dir}/runtime-vendor-unfixed.openvex.json" -printf 'runtime_vulnerabilities=%s\nruntime_vendor_unfixed_low_medium=%s\nruntime_actionable=%s\n' \ - "${total}" "${pending}" "${actionable}" | tee "${output_dir}/runtime-risk-summary.txt" +printf 'runtime_secrets=%s\nruntime_vulnerabilities=%s\nruntime_vendor_unfixed_low_medium=%s\nruntime_actionable=%s\n' \ + "${secrets}" "${total}" "${pending}" "${actionable}" | tee "${output_dir}/runtime-risk-summary.txt" +test "${secrets}" -eq 0 test "${total}" -eq "${pending}" test "${actionable}" -eq 0 test "$(jq '.statements | length' "${output_dir}/runtime-vendor-unfixed.openvex.json")" -eq "${pending}" @@ -53,4 +55,4 @@ jq -e 'all(.statements[]; (.impact_statement | contains("not represented as resolved")))' \ "${output_dir}/runtime-vendor-unfixed.openvex.json" >/dev/null -echo "RESOURCE_SCHEDULER_RUNTIME_RISK_OK total=${total} vendor_unfixed=${pending} actionable=0" +echo "RESOURCE_SCHEDULER_RUNTIME_RISK_OK secrets=0 total=${total} vendor_unfixed=${pending} actionable=0" diff --git a/scripts/check-build-downloads b/scripts/check-build-downloads index 0fa1ba5..90bbb4e 100755 --- a/scripts/check-build-downloads +++ b/scripts/check-build-downloads @@ -98,6 +98,7 @@ require_pattern .github/workflows/supply-chain.yml 'actions/upload-artifact@043f require_pattern .github/workflows/supply-chain.yml 'aquasec/trivy:0\.74\.0@sha256:62b1e65e8869bc4b4c6aa4fa2b21595256c7c2f6018a9d9ad61caf87187c1969' "trivy_image_not_digest_pinned" require_pattern .github/workflows/supply-chain.yml 'govulncheck@v1\.7\.0' "govulncheck_not_version_pinned" require_pattern .github/workflows/supply-chain.yml 'scripts/assess-runtime-vulnerabilities' "runtime_risk_assessment_missing" +require_pattern scripts/assess-runtime-vulnerabilities 'runtime_secrets=%s' "runtime_secret_gate_missing" require_pattern scripts/assess-runtime-vulnerabilities 'runtime_actionable=%s' "runtime_actionable_gate_missing" require_pattern scripts/assess-runtime-vulnerabilities 'status:"under_investigation"' "vendor_unfixed_status_missing" require_pattern .github/workflows/supply-chain.yml "stat -c '%u:%g' bin/resource-scheduler" "output_ownership_gate_missing"