From d85e8a24704d85b9c12f267652533526c48da8c2 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Thu, 1 Oct 2026 23:09:46 +0800 Subject: [PATCH] ci(docker): remove image scans and scan-driven runtime patches --- .github/workflows/beta.yml | 138 +-------------------- .github/workflows/build-v3.yml | 139 +-------------------- .trivyignore.yaml | 85 ------------- docker/Dockerfile | 26 +--- docs/development-setup.md | 18 +-- docs/rules/11-quality-and-security.md | 2 +- docs/v3t-runtime-governance.md | 2 +- tests/test_release_supply_chain.py | 171 +++++--------------------- 8 files changed, 49 insertions(+), 532 deletions(-) delete mode 100644 .trivyignore.yaml diff --git a/.github/workflows/beta.yml b/.github/workflows/beta.yml index 105eabf93..c4dd104a4 100644 --- a/.github/workflows/beta.yml +++ b/.github/workflows/beta.yml @@ -10,9 +10,6 @@ jobs: Docker-build: runs-on: ubuntu-latest name: Build Docker Image - env: - TRIVY_SKIP_DIRS: /usr/share/java - TRIVY_SKIP_JAVA_DB_UPDATE: "true" steps: - name: Checkout uses: actions/checkout@v7 @@ -135,134 +132,6 @@ jobs: - name: Set Up Buildx uses: docker/setup-buildx-action@v4 - - name: Build standard amd64 candidate - uses: docker/build-push-action@v7 - with: - context: . - file: docker/Dockerfile - platforms: linux/amd64 - load: true - push: false - pull: true - tags: moviepilot-v3-candidate:linux-amd64 - build-args: | - MOVIEPILOT_FRONTEND_VERSION=${{ steps.release_version.outputs.frontend_version }} - MOVIEPILOT_FRONTEND_SHA256=${{ steps.payloads.outputs.frontend_sha256 }} - MOVIEPILOT_PLUGINS_REF=${{ steps.payloads.outputs.plugins_revision }} - MOVIEPILOT_RESOURCES_REF=${{ steps.payloads.outputs.resources_revision }} - MOVIEPILOT_PYTHON_VARIANT=standard - cache-from: type=gha,scope=moviepilot-v3-standard-docker-amd64,version=2 - cache-to: type=gha,scope=moviepilot-v3-standard-docker-amd64,mode=max,version=2 - - - name: Scan standard amd64 candidate vulnerabilities - uses: aquasecurity/trivy-action@v0.36.0 - with: - image-ref: moviepilot-v3-candidate:linux-amd64 - version: latest - cache-dir: ${{ runner.temp }}/trivy - scanners: vuln - vuln-type: os,library - severity: HIGH,CRITICAL - ignore-unfixed: true - trivyignores: .trivyignore.yaml - exit-code: 1 - - - name: Build standard arm64 candidate - uses: docker/build-push-action@v7 - with: - context: . - file: docker/Dockerfile - platforms: linux/arm64/v8 - load: true - push: false - pull: true - tags: moviepilot-v3-candidate:linux-arm64 - build-args: | - MOVIEPILOT_FRONTEND_VERSION=${{ steps.release_version.outputs.frontend_version }} - MOVIEPILOT_FRONTEND_SHA256=${{ steps.payloads.outputs.frontend_sha256 }} - MOVIEPILOT_PLUGINS_REF=${{ steps.payloads.outputs.plugins_revision }} - MOVIEPILOT_RESOURCES_REF=${{ steps.payloads.outputs.resources_revision }} - MOVIEPILOT_PYTHON_VARIANT=standard - cache-from: type=gha,scope=moviepilot-v3-standard-docker-arm64,version=2 - cache-to: type=gha,scope=moviepilot-v3-standard-docker-arm64,mode=max,version=2 - - - name: Scan standard arm64 candidate vulnerabilities - uses: aquasecurity/trivy-action@v0.36.0 - with: - image-ref: moviepilot-v3-candidate:linux-arm64 - version: latest - cache-dir: ${{ runner.temp }}/trivy - scanners: vuln - vuln-type: os,library - severity: HIGH,CRITICAL - ignore-unfixed: true - trivyignores: .trivyignore.yaml - exit-code: 1 - - - name: Build free-threaded amd64 candidate - uses: docker/build-push-action@v7 - with: - context: . - file: docker/Dockerfile - platforms: linux/amd64 - load: true - push: false - pull: true - tags: moviepilot-v3t-candidate:linux-amd64 - build-args: | - MOVIEPILOT_FRONTEND_VERSION=${{ steps.release_version.outputs.frontend_version }} - MOVIEPILOT_FRONTEND_SHA256=${{ steps.payloads.outputs.frontend_sha256 }} - MOVIEPILOT_PLUGINS_REF=${{ steps.payloads.outputs.plugins_revision }} - MOVIEPILOT_RESOURCES_REF=${{ steps.payloads.outputs.resources_revision }} - MOVIEPILOT_PYTHON_VARIANT=free-threaded - cache-from: type=gha,scope=moviepilot-v3t-docker-amd64,version=2 - cache-to: type=gha,scope=moviepilot-v3t-docker-amd64,mode=max,version=2 - - - name: Scan free-threaded amd64 candidate vulnerabilities - uses: aquasecurity/trivy-action@v0.36.0 - with: - image-ref: moviepilot-v3t-candidate:linux-amd64 - version: latest - cache-dir: ${{ runner.temp }}/trivy - scanners: vuln - vuln-type: os,library - severity: HIGH,CRITICAL - ignore-unfixed: true - trivyignores: .trivyignore.yaml - exit-code: 1 - - - name: Build free-threaded arm64 candidate - uses: docker/build-push-action@v7 - with: - context: . - file: docker/Dockerfile - platforms: linux/arm64/v8 - load: true - push: false - pull: true - tags: moviepilot-v3t-candidate:linux-arm64 - build-args: | - MOVIEPILOT_FRONTEND_VERSION=${{ steps.release_version.outputs.frontend_version }} - MOVIEPILOT_FRONTEND_SHA256=${{ steps.payloads.outputs.frontend_sha256 }} - MOVIEPILOT_PLUGINS_REF=${{ steps.payloads.outputs.plugins_revision }} - MOVIEPILOT_RESOURCES_REF=${{ steps.payloads.outputs.resources_revision }} - MOVIEPILOT_PYTHON_VARIANT=free-threaded - cache-from: type=gha,scope=moviepilot-v3t-docker-arm64,version=2 - cache-to: type=gha,scope=moviepilot-v3t-docker-arm64,mode=max,version=2 - - - name: Scan free-threaded arm64 candidate vulnerabilities - uses: aquasecurity/trivy-action@v0.36.0 - with: - image-ref: moviepilot-v3t-candidate:linux-arm64 - version: latest - cache-dir: ${{ runner.temp }}/trivy - scanners: vuln - vuln-type: os,library - severity: HIGH,CRITICAL - ignore-unfixed: true - trivyignores: .trivyignore.yaml - exit-code: 1 - - name: Login DockerHub uses: docker/login-action@v4 with: @@ -285,6 +154,7 @@ jobs: linux/amd64 linux/arm64/v8 push: true + pull: true tags: ${{ steps.meta.outputs.tags }} build-args: | MOVIEPILOT_FRONTEND_VERSION=${{ steps.release_version.outputs.frontend_version }} @@ -303,8 +173,10 @@ jobs: org.moviepilot.plugin-market-wiki-revision=${{ steps.plugin_market.outputs.wiki_commit }} org.moviepilot.models-catalog-digest=${{ steps.models_catalog.outputs.digest }} cache-from: | + type=gha,scope=moviepilot-v3-standard-docker,version=2 type=gha,scope=moviepilot-v3-standard-docker-amd64,version=2 type=gha,scope=moviepilot-v3-standard-docker-arm64,version=2 + cache-to: type=gha,scope=moviepilot-v3-standard-docker,mode=max,version=2 - name: Publish free-threaded multi-architecture image uses: docker/build-push-action@v7 @@ -315,7 +187,7 @@ jobs: linux/amd64 linux/arm64/v8 push: true - pull: false + pull: true tags: ${{ steps.meta_ft.outputs.tags }} build-args: | MOVIEPILOT_FRONTEND_VERSION=${{ steps.release_version.outputs.frontend_version }} @@ -334,5 +206,7 @@ jobs: org.moviepilot.plugin-market-wiki-revision=${{ steps.plugin_market.outputs.wiki_commit }} org.moviepilot.models-catalog-digest=${{ steps.models_catalog.outputs.digest }} cache-from: | + type=gha,scope=moviepilot-v3t-docker,version=2 type=gha,scope=moviepilot-v3t-docker-amd64,version=2 type=gha,scope=moviepilot-v3t-docker-arm64,version=2 + cache-to: type=gha,scope=moviepilot-v3t-docker,mode=max,version=2 diff --git a/.github/workflows/build-v3.yml b/.github/workflows/build-v3.yml index 714e51b66..cb8115c06 100644 --- a/.github/workflows/build-v3.yml +++ b/.github/workflows/build-v3.yml @@ -15,9 +15,6 @@ jobs: Docker-build: runs-on: ubuntu-latest name: Build Docker Image - env: - TRIVY_SKIP_DIRS: /usr/share/java - TRIVY_SKIP_JAVA_DB_UPDATE: "true" steps: - name: Checkout uses: actions/checkout@v7 @@ -162,134 +159,6 @@ jobs: - name: Set Up Buildx uses: docker/setup-buildx-action@v4 - - name: Build amd64 candidate - uses: docker/build-push-action@v7 - with: - context: . - file: docker/Dockerfile - platforms: linux/amd64 - load: true - push: false - pull: true - tags: moviepilot-v3-candidate:linux-amd64 - build-args: | - MOVIEPILOT_FRONTEND_VERSION=${{ steps.release_version.outputs.frontend_version }} - MOVIEPILOT_FRONTEND_SHA256=${{ steps.payloads.outputs.frontend_sha256 }} - MOVIEPILOT_PLUGINS_REF=${{ steps.payloads.outputs.plugins_revision }} - MOVIEPILOT_RESOURCES_REF=${{ steps.payloads.outputs.resources_revision }} - MOVIEPILOT_PYTHON_VARIANT=standard - cache-from: type=gha,scope=moviepilot-v3-standard-docker-amd64,version=2 - cache-to: type=gha,scope=moviepilot-v3-standard-docker-amd64,mode=max,version=2 - - - name: Scan amd64 candidate vulnerabilities - uses: aquasecurity/trivy-action@v0.36.0 - with: - image-ref: moviepilot-v3-candidate:linux-amd64 - version: latest - cache-dir: ${{ runner.temp }}/trivy - scanners: vuln - vuln-type: os,library - severity: HIGH,CRITICAL - ignore-unfixed: true - trivyignores: .trivyignore.yaml - exit-code: 1 - - - name: Build arm64 candidate - uses: docker/build-push-action@v7 - with: - context: . - file: docker/Dockerfile - platforms: linux/arm64/v8 - load: true - push: false - pull: true - tags: moviepilot-v3-candidate:linux-arm64 - build-args: | - MOVIEPILOT_FRONTEND_VERSION=${{ steps.release_version.outputs.frontend_version }} - MOVIEPILOT_FRONTEND_SHA256=${{ steps.payloads.outputs.frontend_sha256 }} - MOVIEPILOT_PLUGINS_REF=${{ steps.payloads.outputs.plugins_revision }} - MOVIEPILOT_RESOURCES_REF=${{ steps.payloads.outputs.resources_revision }} - MOVIEPILOT_PYTHON_VARIANT=standard - cache-from: type=gha,scope=moviepilot-v3-standard-docker-arm64,version=2 - cache-to: type=gha,scope=moviepilot-v3-standard-docker-arm64,mode=max,version=2 - - - name: Scan arm64 candidate vulnerabilities - uses: aquasecurity/trivy-action@v0.36.0 - with: - image-ref: moviepilot-v3-candidate:linux-arm64 - version: latest - cache-dir: ${{ runner.temp }}/trivy - scanners: vuln - vuln-type: os,library - severity: HIGH,CRITICAL - ignore-unfixed: true - trivyignores: .trivyignore.yaml - exit-code: 1 - - - name: Build free-threaded amd64 candidate - uses: docker/build-push-action@v7 - with: - context: . - file: docker/Dockerfile - platforms: linux/amd64 - load: true - push: false - pull: true - tags: moviepilot-v3t-candidate:linux-amd64 - build-args: | - MOVIEPILOT_FRONTEND_VERSION=${{ steps.release_version.outputs.frontend_version }} - MOVIEPILOT_FRONTEND_SHA256=${{ steps.payloads.outputs.frontend_sha256 }} - MOVIEPILOT_PLUGINS_REF=${{ steps.payloads.outputs.plugins_revision }} - MOVIEPILOT_RESOURCES_REF=${{ steps.payloads.outputs.resources_revision }} - MOVIEPILOT_PYTHON_VARIANT=free-threaded - cache-from: type=gha,scope=moviepilot-v3t-docker-amd64,version=2 - cache-to: type=gha,scope=moviepilot-v3t-docker-amd64,mode=max,version=2 - - - name: Scan free-threaded amd64 candidate vulnerabilities - uses: aquasecurity/trivy-action@v0.36.0 - with: - image-ref: moviepilot-v3t-candidate:linux-amd64 - version: latest - cache-dir: ${{ runner.temp }}/trivy - scanners: vuln - vuln-type: os,library - severity: HIGH,CRITICAL - ignore-unfixed: true - trivyignores: .trivyignore.yaml - exit-code: 1 - - - name: Build free-threaded arm64 candidate - uses: docker/build-push-action@v7 - with: - context: . - file: docker/Dockerfile - platforms: linux/arm64/v8 - load: true - push: false - pull: true - tags: moviepilot-v3t-candidate:linux-arm64 - build-args: | - MOVIEPILOT_FRONTEND_VERSION=${{ steps.release_version.outputs.frontend_version }} - MOVIEPILOT_FRONTEND_SHA256=${{ steps.payloads.outputs.frontend_sha256 }} - MOVIEPILOT_PLUGINS_REF=${{ steps.payloads.outputs.plugins_revision }} - MOVIEPILOT_RESOURCES_REF=${{ steps.payloads.outputs.resources_revision }} - MOVIEPILOT_PYTHON_VARIANT=free-threaded - cache-from: type=gha,scope=moviepilot-v3t-docker-arm64,version=2 - cache-to: type=gha,scope=moviepilot-v3t-docker-arm64,mode=max,version=2 - - - name: Scan free-threaded arm64 candidate vulnerabilities - uses: aquasecurity/trivy-action@v0.36.0 - with: - image-ref: moviepilot-v3t-candidate:linux-arm64 - version: latest - cache-dir: ${{ runner.temp }}/trivy - scanners: vuln - vuln-type: os,library - severity: HIGH,CRITICAL - ignore-unfixed: true - trivyignores: .trivyignore.yaml - exit-code: 1 - - name: Login DockerHub uses: docker/login-action@v4 with: @@ -312,7 +181,7 @@ jobs: linux/amd64 linux/arm64/v8 push: true - pull: false + pull: true tags: ${{ steps.meta.outputs.tags }} build-args: | MOVIEPILOT_FRONTEND_VERSION=${{ steps.release_version.outputs.frontend_version }} @@ -332,8 +201,10 @@ jobs: org.moviepilot.plugin-market-wiki-revision=${{ steps.plugin_market.outputs.wiki_commit }} org.moviepilot.models-catalog-digest=${{ steps.models_catalog.outputs.digest }} cache-from: | + type=gha,scope=moviepilot-v3-standard-docker,version=2 type=gha,scope=moviepilot-v3-standard-docker-amd64,version=2 type=gha,scope=moviepilot-v3-standard-docker-arm64,version=2 + cache-to: type=gha,scope=moviepilot-v3-standard-docker,mode=max,version=2 - name: Publish free-threaded multi-architecture image uses: docker/build-push-action@v7 @@ -344,7 +215,7 @@ jobs: linux/amd64 linux/arm64/v8 push: true - pull: false + pull: true tags: ${{ steps.meta_ft.outputs.tags }} build-args: | MOVIEPILOT_FRONTEND_VERSION=${{ steps.release_version.outputs.frontend_version }} @@ -364,8 +235,10 @@ jobs: org.moviepilot.plugin-market-wiki-revision=${{ steps.plugin_market.outputs.wiki_commit }} org.moviepilot.models-catalog-digest=${{ steps.models_catalog.outputs.digest }} cache-from: | + type=gha,scope=moviepilot-v3t-docker,version=2 type=gha,scope=moviepilot-v3t-docker-amd64,version=2 type=gha,scope=moviepilot-v3t-docker-arm64,version=2 + cache-to: type=gha,scope=moviepilot-v3t-docker,mode=max,version=2 - name: Generate Changelog id: changelog diff --git a/.trivyignore.yaml b/.trivyignore.yaml deleted file mode 100644 index 26ab62c21..000000000 --- a/.trivyignore.yaml +++ /dev/null @@ -1,85 +0,0 @@ -vulnerabilities: - - id: CVE-2026-84445 - paths: - - usr/bin/rclone - purls: - - pkg:golang/google.golang.org/grpc@v1.84.0-dev.0.20260723093437-b6eac429d7b6 - expired_at: 2026-10-09 - statement: The pinned rclone image d6f5448594ecefefcf09cfeaf85cb7a21a866328032576ce2c1813e7b59c66dc has no google.golang.org/grpc/xds functions in either its amd64 or arm64 Go function table. GHSA-2v4p-qf9q-27wj requires xds.NewGRPCServer, so the affected interceptor is not linked. Reassess this exception when upgrading rclone or before expiry; see docs/development-setup.md. - - id: GHSA-6v7p-g79w-8964 - paths: - - Python - purls: - - pkg:pypi/msgpack@1.1.2 - expired_at: 2026-11-20 - statement: The finding belongs to the base image's system pip and is not imported by MoviePilot. - - id: CVE-2025-47273 - paths: - - Python - purls: - - pkg:pypi/setuptools@70.3.0 - expired_at: 2026-11-20 - statement: The finding belongs to the base image's system pip and is not used for dependency installation. - - id: CVE-2026-33818 - paths: - - usr/bin/rclone - purls: - - pkg:golang/stdlib@v1.26.5 - expired_at: 2026-11-20 - statement: The official rclone binary has no patched release for this embedded Go runtime yet. - - id: CVE-2026-39821 - paths: - - usr/bin/rclone - purls: - - pkg:golang/stdlib@v1.26.5 - expired_at: 2026-11-20 - statement: The official rclone binary has no patched release for this embedded Go runtime yet. - - id: CVE-2026-46600 - paths: - - usr/bin/rclone - purls: - - pkg:golang/stdlib@v1.26.5 - expired_at: 2026-11-20 - statement: The official rclone binary has no patched release for this embedded Go runtime yet. - - id: CVE-2026-56853 - paths: - - usr/bin/rclone - purls: - - pkg:golang/stdlib@v1.26.5 - expired_at: 2026-11-20 - statement: The official rclone binary has no patched release for this embedded Go runtime yet. - - id: CVE-2026-56854 - paths: - - usr/bin/rclone - purls: - - pkg:golang/golang.org/x/crypto@v0.54.0 - expired_at: 2026-11-20 - statement: The pinned stable rclone image has no release with the upstream x/crypto v0.55.0 fix yet; its SFTP callbacks do not set source-address permissions, so the affected path is not reachable. Reassess when this exception expires. - - id: CVE-2026-56858 - paths: - - usr/bin/rclone - purls: - - pkg:golang/stdlib@v1.26.5 - expired_at: 2026-11-20 - statement: The official rclone binary has no patched release for this embedded Go runtime yet. - - id: CVE-2026-56859 - paths: - - usr/bin/rclone - purls: - - pkg:golang/stdlib@v1.26.5 - expired_at: 2026-11-20 - statement: The official rclone binary has no patched release for this embedded Go runtime yet. - - id: CVE-2026-56860 - paths: - - usr/bin/rclone - purls: - - pkg:golang/stdlib@v1.26.5 - expired_at: 2026-11-20 - statement: The official rclone binary has no patched release for this embedded Go runtime yet. - - id: CVE-2026-56862 - paths: - - usr/bin/rclone - purls: - - pkg:golang/stdlib@v1.26.5 - expired_at: 2026-11-20 - statement: The official rclone binary has no patched release for this embedded Go runtime yet. diff --git a/docker/Dockerfile b/docker/Dockerfile index 09bf44ae4..e6d31158b 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -6,8 +6,7 @@ ARG MOVIEPILOT_PYTHON_VERSION="3.14.7" FROM ghcr.io/astral-sh/uv:latest AS uv -# rclone v1.75.0 包含受 CVE-2026-46603 影响的 x/image v0.44.0,临时固定已修复的官方 Beta 摘要。 -FROM rclone/rclone:beta@sha256:d6f5448594ecefefcf09cfeaf85cb7a21a866328032576ce2c1813e7b59c66dc AS rclone +FROM rclone/rclone:latest AS rclone FROM mwader/static-ffmpeg:8.1.1@sha256:735f84b905e00d5c618b667f0b053f83b1096f5fc404c607e6134bf2275a0e0a AS ffmpeg @@ -89,11 +88,9 @@ RUN apt-get update \ lsof \ nano \ unar \ - openssl \ supervisor \ postgresql-client-18 \ libchromaprint-tools \ - libevent-2.1-7t64 \ libjemalloc2 \ && dpkg-reconfigure --frontend noninteractive tzdata \ && ln -s /usr/lib/*-linux-gnu/libjemalloc.so.2 /usr/local/lib/libjemalloc.so \ @@ -165,8 +162,7 @@ RUN apt-get update \ --no-default-groups \ --group runtime-free-threaded \ --no-install-project \ - --python "${python_bin}" \ - && "${python_bin}" -m pip uninstall --break-system-packages -y pip + --python "${python_bin}" FROM prepare_venv_standard AS verify_venv_standard @@ -286,14 +282,6 @@ RUN mkdir -p \ # final 阶段: 安装运行时依赖和配置最终镜像 FROM prepare_package AS final -# 运行时使用独立虚拟环境和 uv,不需要基础镜像自带的系统 pip;移除它及其 -# vendored 依赖,避免将已修复版本之外的 urllib3 打包进最终镜像。 -RUN python3 -m pip uninstall -y pip \ - && rm -rf /usr/local/lib/python3.14/site-packages/pip \ - /usr/local/lib/python3.14/site-packages/pip-*.dist-info \ - /usr/local/lib/python3.14/site-packages/pip-*.egg-info \ - && rm -f /usr/local/bin/pip /usr/local/bin/pip3 /usr/local/bin/pip3.14 - # jemalloc 默认按 CPU 数×4 建 arena,空闲 arena 的脏页会长期计入 RSS。脏页按 5 秒衰减归还 # (muzzy 直接归还,避免 MADV_FREE 页继续计入 RSS);arena 限制为 8 个,带 GIL 的 Python 大块分配竞争很小。 # 后台回收线程由主进程启动时自行开启,不能写在这里:子进程继承 MALLOC_CONF 后 Chromium 无法启动。 @@ -313,16 +301,6 @@ COPY --from=uv /uv /usr/local/bin/uv # 浏览器运行依赖 RUN playwright install-deps chromium \ - && apt-get update \ - && apt-get install -y --no-install-recommends \ - gzip \ - libde265-0 \ - libpcre2-8-0 \ - libsqlite3-0 \ - nginx \ - nginx-common \ - xserver-common \ - xvfb \ && apt-get autoremove -y \ && apt-get clean \ && rm -rf \ diff --git a/docs/development-setup.md b/docs/development-setup.md index 6b0aede88..949701be0 100644 --- a/docs/development-setup.md +++ b/docs/development-setup.md @@ -202,21 +202,9 @@ uvx --from pip-audit pip-audit \ 导出文件由 `uv.lock` 生成且保留哈希,不作为项目依赖清单提交。 -Docker 镜像发布前还会使用 Trivy 扫描 OS 与语言包;根目录 `.trivyignore.yaml` 只允许记录按路径或 PURL -限定、写明原因并设置到期时间的临时例外,修复或重新评估后应移除。 - -`CVE-2026-84445` 临时例外的核查记录(2026-09-09,2026-10-09 到期): - -- [上游公告](https://github.com/grpc/grpc-go/security/advisories/GHSA-2v4p-qf9q-27wj) 将触发条件限定为 - `xds.NewGRPCServer()` 安装的 xDS 路由拦截器;普通 gRPC 依赖的存在不代表包含该路径。 -- 当前固定的 `rclone/rclone:beta@sha256:d6f5448594ecefefcf09cfeaf85cb7a21a866328032576ce2c1813e7b59c66dc` - 对应 `v1.76.0-beta.10267.220fe7619`。从该摘要提取两个架构的 `/usr/local/bin/rclone`, - 使用 `go version -m` 确认内嵌 gRPC 为 `v1.84.0-dev.0.20260723093437-b6eac429d7b6`。 -- 使用 Go 标准库 `debug/elf` 和 `debug/gosym` 解析二进制 `.gopclntab`:amd64 共 91,641 个函数, - arm64 共 91,150 个函数;各有 1,643 个 gRPC 函数,均无 `google.golang.org/grpc/xds` - 或 `google.golang.org/grpc/internal/xds` 函数,未链接受影响的服务端拦截器。 -- 核查时官方稳定版 `v1.75.1` 和主分支仍引用同一 gRPC 版本。例外只匹配镜像内 `usr/bin/rclone` - 和上述精确依赖 PURL;更新 rclone 摘要时必须重新核查两个架构,包含修复后应移除例外。 +Docker 镜像直接基于官方基础镜像构建,只安装应用运行所需的系统依赖;不运行镜像漏洞扫描, +也不为扫描结果刷新系统包、删除基础镜像自带工具或维护漏洞忽略清单。正式版和 Beta 均直接构建 +并发布标准版与 free-threaded 版的双架构镜像。应用锁定依赖仍按上述流程审计。 ### 6. Contributor 提交准备 diff --git a/docs/rules/11-quality-and-security.md b/docs/rules/11-quality-and-security.md index 81196c44f..511a71cad 100644 --- a/docs/rules/11-quality-and-security.md +++ b/docs/rules/11-quality-and-security.md @@ -66,7 +66,7 @@ uvx --from pip-audit pip-audit \ - Run after runtime dependency changes; the release workflow audits the same locked dependency set before publishing images. - Any Python vulnerability reported by this audit blocks publishing until the dependency or explicit audit policy is updated. -- Release candidates also scan OS and language packages on amd64 and arm64. HIGH or CRITICAL findings with an available fix block publishing; unfixed upstream findings require a separate reachability and impact assessment. +- Docker builds use official base images and install required runtime packages only. Image vulnerability scans and scan-driven system-package patches are not part of the release process; the locked Python dependency audit remains required. - If upstream has no fix, assess reachability and impact before changing the audit policy; PR documentation alone does not bypass the gate. --- diff --git a/docs/v3t-runtime-governance.md b/docs/v3t-runtime-governance.md index da7d2f33c..b44d060f0 100644 --- a/docs/v3t-runtime-governance.md +++ b/docs/v3t-runtime-governance.md @@ -246,7 +246,7 @@ arm64 和 Windows x64 的结论一致:安装器能够把磁盘扩展替换为 5. **执行同 revision A/B**:先证明标准 V3 修改前后无可重复退化,再比较同一 revision、同一资源限制 和不可变镜像 digest 的 V3/V3t。至少保留镜像体积、启动、RSS/PSS/USS、API p50/p95、SQLite、 PostgreSQL 和 CPU 热点原始样本。 -6. **完成双架构验收**:amd64、arm64 的构建、漏洞扫描、启动和核心功能均通过后,才发布候选制品。 +6. **完成双架构验收**:amd64、arm64 的构建、锁定依赖检查、启动和核心功能均通过后,才发布候选制品。 7. **删除临时处理**:同一变更中移除不再需要的 source、版本分叉或排除规则,并更新本表、锁文件、 构建探针和回归测试;不要永久保留失效的兼容分支。 diff --git a/tests/test_release_supply_chain.py b/tests/test_release_supply_chain.py index a6aa01cec..1eb52817d 100644 --- a/tests/test_release_supply_chain.py +++ b/tests/test_release_supply_chain.py @@ -3,7 +3,6 @@ import os import shutil import subprocess -from datetime import date from pathlib import Path import pytest @@ -17,7 +16,6 @@ RELEASE_WORKFLOW = ROOT / ".github" / "workflows" / "build-v3.yml" BETA_WORKFLOW = ROOT / ".github" / "workflows" / "beta.yml" PR_AGENT_WORKFLOW = ROOT / ".github" / "workflows" / "pr-agent.yml" CODEX_EVENT_WORKFLOW = ROOT / ".github" / "workflows" / "moviepilot-codex-events.yml" -TRIVY_IGNORE = ROOT / ".trivyignore.yaml" WORKFLOW_ROOT = ROOT / ".github" / "workflows" ALLOWED_ACTION_REFS = { @@ -31,7 +29,6 @@ ALLOWED_ACTION_REFS = { "docker/setup-buildx-action@v4", "docker/build-push-action@v7", "docker/login-action@v4", - "aquasecurity/trivy-action@v0.36.0", "actions/upload-artifact@v7", "actions/download-artifact@v8", "docker://ghcr.io/infinitypacer/pr-review-runner:latest", @@ -114,43 +111,22 @@ def _run_release_script( def test_base_image_uses_refreshable_tag_and_apt_does_not_upgrade_in_place() -> None: - """基础镜像允许更新,并仅显式刷新运行时安全包而非整套 Debian。""" + """使用官方基础镜像及所需运行依赖,不追加系统包扫描补丁。""" dockerfile = DOCKERFILE.read_text(encoding="utf-8") - assert 'ARG MOVIEPILOT_PYTHON_VERSION="3.14.7"' in dockerfile assert "FROM python:${MOVIEPILOT_PYTHON_VERSION}-slim-trixie AS base" in dockerfile - assert "python:${MOVIEPILOT_PYTHON_VERSION}-slim-trixie@sha256:" not in dockerfile - free_threaded_stage = dockerfile.split( - "FROM prepare_venv_common AS prepare_venv_free-threaded", - maxsplit=1, - )[1] - assert "ARG MOVIEPILOT_PYTHON_VERSION" in free_threaded_stage - assert 'uv python install --no-bin "${MOVIEPILOT_PYTHON_VERSION}t"' in free_threaded_stage + assert 'uv python install --no-bin "${MOVIEPILOT_PYTHON_VERSION}t"' in dockerfile assert "apt-get upgrade" not in dockerfile - assert "apt-get install -y --no-install-recommends \\\n gzip \\\n" in dockerfile - assert "\n libevent-2.1-7t64 \\\n" in dockerfile - assert "\n openssl \\\n" in dockerfile - assert "\n util-linux \\\n" in dockerfile - for package in ( - "gzip", - "libde265-0", - "libpcre2-8-0", - "libsqlite3-0", - "nginx", - "nginx-common", - "xserver-common", - "xvfb", - ): - assert f"\n {package} \\\n" in dockerfile + assert "pip uninstall" not in dockerfile + assert "break-system-packages" not in dockerfile + browser_install = dockerfile.split("RUN playwright install-deps chromium", maxsplit=1)[1] + assert "apt-get install" not in browser_install -def test_rclone_image_uses_cve_2026_46603_patched_build() -> None: - """rclone 制品必须固定到包含 x/image 漏洞修复的不可变镜像。""" +def test_rclone_image_uses_official_stable_channel() -> None: + """rclone 使用官方稳定镜像,不固定用于应对扫描的临时 Beta。""" dockerfile = DOCKERFILE.read_text(encoding="utf-8") - - assert "rclone/rclone:1.75.0" not in dockerfile - patched_rclone_image = "rclone/rclone:beta@sha256:d6f5448594ecefefcf09cfeaf85cb7a21a866328032576ce2c1813e7b59c66dc" - assert f"FROM {patched_rclone_image} AS rclone" in dockerfile + assert "FROM rclone/rclone:latest AS rclone" in dockerfile def test_release_audits_locked_runtime_dependencies_before_building() -> None: @@ -161,8 +137,8 @@ def test_release_audits_locked_runtime_dependencies_before_building() -> None: names = [step.get("name") for step in steps] audit = _steps_by_name(workflow)["Audit locked Python dependencies"]["run"] - first_candidate = next(name for name in names if name and name.startswith("Build ")) - assert names.index("Audit locked Python dependencies") < names.index(first_candidate) + first_publish = next(name for name in names if name and name.startswith("Publish ")) + assert names.index("Audit locked Python dependencies") < names.index(first_publish) assert "--group runtime-standard" in audit assert "--group runtime-free-threaded" in audit assert "scripts/normalize_audit_requirements.py" in audit @@ -206,71 +182,20 @@ def test_direct_url_audit_requirement_rejects_unlocked_source(tmp_path: Path) -> normalize_requirements("demo @ https://example.com/demo.tar.gz\n", lock_file) -def test_release_scans_both_architectures_before_registry_login_and_publish() -> None: - """两个 Python 变体的各架构扫描都必须在登录仓库和发布前完成。""" - workflow = _load_workflow() - trivy_env = workflow["jobs"]["Docker-build"]["env"] - assert trivy_env["TRIVY_SKIP_DIRS"] == "/usr/share/java" - assert trivy_env["TRIVY_SKIP_JAVA_DB_UPDATE"] == "true" +@pytest.mark.parametrize("path", (RELEASE_WORKFLOW, BETA_WORKFLOW)) +def test_release_builds_publish_both_architectures_directly(path: Path) -> None: + """正式版和 Beta 直接构建发布双架构产物,删除扫描专用候选流程。""" + workflow = _load_workflow(path) steps = workflow["jobs"]["Docker-build"]["steps"] - names = [step.get("name") for step in steps] - indexed = _steps_by_name(workflow) - - expected_candidates = { - "Build amd64 candidate": ("linux/amd64", "moviepilot-v3-candidate:linux-amd64"), - "Build arm64 candidate": ("linux/arm64/v8", "moviepilot-v3-candidate:linux-arm64"), - "Build free-threaded amd64 candidate": ( - "linux/amd64", - "moviepilot-v3t-candidate:linux-amd64", - ), - "Build free-threaded arm64 candidate": ( - "linux/arm64/v8", - "moviepilot-v3t-candidate:linux-arm64", - ), - } - for name, (platform, tag) in expected_candidates.items(): - build = indexed[name]["with"] - assert build["platforms"] == platform - assert build["load"] is True - assert build["push"] is False - assert build["tags"] == tag - assert build["pull"] is True - assert "no-cache-filters" not in build - expected_variant = "free-threaded" if "free-threaded" in name else "standard" - assert f"MOVIEPILOT_PYTHON_VARIANT={expected_variant}" in build["build-args"] - - for name in ( - "Scan amd64 candidate vulnerabilities", - "Scan arm64 candidate vulnerabilities", - "Scan free-threaded amd64 candidate vulnerabilities", - "Scan free-threaded arm64 candidate vulnerabilities", - ): - scan = indexed[name] - assert scan["with"]["cache-dir"] == "${{ runner.temp }}/trivy" - assert scan["uses"] == "aquasecurity/trivy-action@v0.36.0" - assert scan["with"].items() >= { - "version": "latest", - "scanners": "vuln", - "vuln-type": "os,library", - "severity": "HIGH,CRITICAL", - "ignore-unfixed": True, - "trivyignores": ".trivyignore.yaml", - "exit-code": 1, - }.items() - - last_scan = max( - names.index(name) - for name in ( - "Scan amd64 candidate vulnerabilities", - "Scan arm64 candidate vulnerabilities", - "Scan free-threaded amd64 candidate vulnerabilities", - "Scan free-threaded arm64 candidate vulnerabilities", - ) - ) - assert last_scan < names.index("Login DockerHub") - assert last_scan < names.index("Login GitHub Container Registry") - assert last_scan < names.index("Publish multi-architecture image") - assert last_scan < names.index("Publish free-threaded multi-architecture image") + builds = [step for step in steps if step.get("uses") == "docker/build-push-action@v7"] + assert len(builds) == 2 + for step in builds: + config = step["with"] + assert config["push"] is True + assert config["pull"] is True + assert set(config["platforms"].split()) == {"linux/amd64", "linux/arm64/v8"} + assert "cache-to" in config + assert all("trivy" not in step.get("uses", "") for step in steps) def test_workflows_follow_maintained_action_channels() -> None: @@ -425,29 +350,14 @@ def test_dependency_compat_checks_minimum_uv_version() -> None: assert "assert" in command -def test_vulnerability_ignores_are_scoped_justified_and_time_bounded() -> None: - """漏洞豁免必须限定制品范围,并保留复查期限和接受理由。""" - yaml = YAML(typ="safe") - vulnerabilities = yaml.load(TRIVY_IGNORE.read_text(encoding="utf-8"))["vulnerabilities"] - - for vulnerability in vulnerabilities: - assert vulnerability["paths"] - assert vulnerability["purls"] - assert vulnerability["statement"] - assert isinstance(vulnerability["expired_at"], date) - - -def test_publish_reuses_scanned_architecture_caches_without_refreshing_base() -> None: - """发布构建复用已扫描候选缓存,不得在扫描后重新拉取未审计基础镜像。""" +def test_publish_refreshes_base_and_preserves_build_cache() -> None: + """直接发布时拉取基础镜像,同时读写双架构构建缓存。""" workflow = _load_workflow() publish = _steps_by_name(workflow)["Publish multi-architecture image"]["with"] - assert workflow["on"]["workflow_dispatch"] is None - assert publish["platforms"] == "linux/amd64\nlinux/arm64/v8\n" assert publish["push"] is True - assert publish["pull"] is False - assert "scope=moviepilot-v3-standard-docker-amd64" in publish["cache-from"] - assert "scope=moviepilot-v3-standard-docker-arm64" in publish["cache-from"] + assert publish["pull"] is True + assert "scope=moviepilot-v3-standard-docker," in publish["cache-to"] def test_release_publishes_free_threaded_image_with_separate_metadata_and_cache() -> None: @@ -488,38 +398,17 @@ def test_release_publishes_version_and_latest_tags_for_both_image_variants() -> assert "ghcr.io/${{ github.repository }}" in standard_images -def test_beta_applies_the_same_variant_scan_and_publish_contract() -> None: - """Beta 也必须在发布两个变体前完成各架构漏洞扫描。""" +def test_beta_applies_the_same_variant_publish_contract() -> None: + """Beta 保持两个变体的发布参数、标签和缓存隔离。""" workflow = _load_workflow(BETA_WORKFLOW) - trivy_env = workflow["jobs"]["Docker-build"]["env"] - assert trivy_env["TRIVY_SKIP_DIRS"] == "/usr/share/java" - assert trivy_env["TRIVY_SKIP_JAVA_DB_UPDATE"] == "true" steps = workflow["jobs"]["Docker-build"]["steps"] names = [step.get("name") for step in steps] indexed = _steps_by_name(workflow) - assert workflow["on"]["workflow_dispatch"] is None - for name in ( - "Build standard amd64 candidate", - "Build standard arm64 candidate", - "Build free-threaded amd64 candidate", - "Build free-threaded arm64 candidate", - ): - assert indexed[name]["with"]["load"] is True - assert indexed[name]["with"]["push"] is False - - scan_names = ( - "Scan standard amd64 candidate vulnerabilities", - "Scan standard arm64 candidate vulnerabilities", - "Scan free-threaded amd64 candidate vulnerabilities", - "Scan free-threaded arm64 candidate vulnerabilities", - ) publish_names = ( "Publish standard multi-architecture image", "Publish free-threaded multi-architecture image", ) - last_scan = max(names.index(name) for name in scan_names) - assert all(last_scan < names.index(name) for name in publish_names) assert "MOVIEPILOT_PYTHON_VARIANT=standard" in indexed[publish_names[0]]["with"]["build-args"] assert "MOVIEPILOT_PYTHON_VARIANT=free-threaded" in indexed[publish_names[1]]["with"]["build-args"] assert "scope=moviepilot-v3-standard-docker-amd64" in indexed[publish_names[0]]["with"]["cache-from"]