From c67ef216f0ecc33b4de217855099c8c497cc1a41 Mon Sep 17 00:00:00 2001 From: tianM <46552064+a3384379@users.noreply.github.com> Date: Sat, 3 Oct 2026 06:11:29 +0800 Subject: [PATCH] fix(security): patch runtime dependencies and pin repaired environment fixture --- .github/workflows/compose-ci.yml | 2 +- .github/workflows/security-ci.yml | 4 +-- backend/Dockerfile | 10 +++++++ backend/tests/test_environment_lab.py | 4 +-- backend/uv.lock | 6 ++-- ...environment-fixture-security-2026-10-03.md | 30 +++++++++++++++++++ frontend/Dockerfile | 2 +- frontend/package.json | 2 +- frontend/pnpm-lock.yaml | 18 +++++------ .../src/pages/EnvironmentLabPage.test.tsx | 2 +- frontend/src/pages/EnvironmentLabPage.tsx | 2 +- mock-target/Dockerfile | 10 +++++++ scripts/smoke_s26.py | 4 +-- 13 files changed, 73 insertions(+), 23 deletions(-) create mode 100644 docs/operations/environment-fixture-security-2026-10-03.md diff --git a/.github/workflows/compose-ci.yml b/.github/workflows/compose-ci.yml index 7587e530..1e2b4b3d 100644 --- a/.github/workflows/compose-ci.yml +++ b/.github/workflows/compose-ci.yml @@ -195,7 +195,7 @@ jobs: FLOWTEST_FEATURE_EVENT_PROTOCOLS_ENABLED: "true" FLOWTEST_FEATURE_PERFORMANCE_LAB_ENABLED: "true" FLOWTEST_FEATURE_ENVIRONMENT_LAB_ENABLED: "true" - FLOWTEST_ENVIRONMENT_IMAGE_ALLOWLIST: '["docker.io/nginxinc/nginx-unprivileged@sha256:123fb7283ffb4788e260d4e980005a978995fefedcdbb04d268077a19b84576d"]' + FLOWTEST_ENVIRONMENT_IMAGE_ALLOWLIST: '["cgr.dev/chainguard/nginx@sha256:a104d1995e56b7a15e8f152078dfbdb1ecbf9f9d1af311e7906e7b4c0c790cf2"]' FLOWTEST_AI_BASE_URL: http://mock-target:8080/v1 FLOWTEST_AI_MODEL: flowtest-compose-model FLOWTEST_AI_API_KEY: flowtest-mock-ai-key diff --git a/.github/workflows/security-ci.yml b/.github/workflows/security-ci.yml index acc0759b..2408bb6f 100644 --- a/.github/workflows/security-ci.yml +++ b/.github/workflows/security-ci.yml @@ -82,8 +82,8 @@ jobs: done - name: Prepare pinned environment fixture run: | - docker pull docker.io/nginxinc/nginx-unprivileged@sha256:123fb7283ffb4788e260d4e980005a978995fefedcdbb04d268077a19b84576d - docker tag docker.io/nginxinc/nginx-unprivileged@sha256:123fb7283ffb4788e260d4e980005a978995fefedcdbb04d268077a19b84576d flowtest-environment-fixture:ci + docker pull cgr.dev/chainguard/nginx@sha256:a104d1995e56b7a15e8f152078dfbdb1ecbf9f9d1af311e7906e7b4c0c790cf2 + docker tag cgr.dev/chainguard/nginx@sha256:a104d1995e56b7a15e8f152078dfbdb1ecbf9f9d1af311e7906e7b4c0c790cf2 flowtest-environment-fixture:ci - name: Scan backend image uses: anchore/scan-action@e1165082ffb1fe366ebaf02d8526e7c4989ea9d2 # v7.4.0 with: diff --git a/backend/Dockerfile b/backend/Dockerfile index d0ae0b15..bde821de 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -140,6 +140,7 @@ RUN set -eux; \ ctr --version FROM ${FLOWTEST_ENVIRONMENT_DAEMON_BASE} AS environment-daemon +RUN apk add --no-cache --upgrade 'pcre2>=10.49-r0' 'libexpat>=2.8.5-r0' FROM debian:trixie-slim AS python-base-source @@ -244,6 +245,15 @@ ENV PYTHONDONTWRITEBYTECODE=1 \ PIP_NO_CACHE_DIR=1 FROM ${FLOWTEST_PYTHON_BASE} AS python-runtime +# Refresh fixed Debian libraries after selecting the reusable Python base. +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends --only-upgrade \ + libssl3t64 openssl-provider-legacy libpcre2-8-0; \ + dpkg --compare-versions "$(dpkg-query -W -f='${Version}' libssl3t64)" ge '3.5.7-1~deb13u3'; \ + dpkg --compare-versions "$(dpkg-query -W -f='${Version}' openssl-provider-legacy)" ge '3.5.7-1~deb13u3'; \ + dpkg --compare-versions "$(dpkg-query -W -f='${Version}' libpcre2-8-0)" ge '10.46-1~deb13u3'; \ + apt-get dist-clean WORKDIR /app COPY --from=uv /uv /uvx /bin/ diff --git a/backend/tests/test_environment_lab.py b/backend/tests/test_environment_lab.py index d43c533e..e7476a40 100644 --- a/backend/tests/test_environment_lab.py +++ b/backend/tests/test_environment_lab.py @@ -42,8 +42,8 @@ USER_EMAIL = "environment-user@example.com" USER_PASSWORD = "environment-user-password-123!" FIXTURE_IMAGE = ( - "docker.io/nginxinc/nginx-unprivileged@sha256:" - "123fb7283ffb4788e260d4e980005a978995fefedcdbb04d268077a19b84576d" + "cgr.dev/chainguard/nginx@sha256:" + "a104d1995e56b7a15e8f152078dfbdb1ecbf9f9d1af311e7906e7b4c0c790cf2" ) diff --git a/backend/uv.lock b/backend/uv.lock index e192ea00..00d68ee1 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -2261,11 +2261,11 @@ wheels = [ [[package]] name = "urllib3" -version = "2.7.0" +version = "2.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/05/b17359e1cefb4f909b5e40b1b90a496d987258916dbbf88e842c729f510e/urllib3-2.8.0.tar.gz", hash = "sha256:63bf2ead4c879426ebf22ef2a781eeb4aa3b4ae798a0435506f8687fd5bb9b63", size = 458972, upload-time = "2026-09-15T19:29:36.253Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, + { url = "https://files.pythonhosted.org/packages/92/9d/c4e665119135114480843e7ab388fa94d8480650450e6f8e26b70d323a4c/urllib3-2.8.0-py3-none-any.whl", hash = "sha256:0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3", size = 135717, upload-time = "2026-09-15T19:29:34.577Z" }, ] [[package]] diff --git a/docs/operations/environment-fixture-security-2026-10-03.md b/docs/operations/environment-fixture-security-2026-10-03.md new file mode 100644 index 00000000..940e3ff1 --- /dev/null +++ b/docs/operations/environment-fixture-security-2026-10-03.md @@ -0,0 +1,30 @@ +# 环境夹具与运行时安全更新(2026-10-03) + +## 问题与修改范围 + +固定的 Nginx 环境夹具包含 PCRE2 10.48,现有 High / Critical 门禁报告可修复的 CVE-2026-103111。官方最新 Alpine 与 Debian 镜像的实际依赖仍未消除当前扫描阻塞。 + +将环境实验室默认示例、前后端测试、S26 验收脚本、Compose 允许列表及安全扫描统一更新为以下多架构不可变镜像: + +```text +cgr.dev/chainguard/nginx@sha256:a104d1995e56b7a15e8f152078dfbdb1ecbf9f9d1af311e7906e7b4c0c790cf2 +``` + +该镜像可匿名拉取,amd64 与 arm64 均包含 PCRE2 10.49-r1,使用 UID / GID 65532,默认监听 8080。[供应方兼容说明](https://images.chainguard.dev/directory/image/nginx/overview)列出非 root 用户、端口及只读根文件系统所需的 `/run`、`/var/lib/nginx/tmp`,与现有环境执行器的固定安全参数一致。 + +同时纳入本次门禁已定位的依赖修复:urllib3 2.8.0、Axios 1.20.0 / follow-redirects 1.16.1;Python 与 Mock 镜像在选择基础镜像后安装已修复的 Debian OpenSSL / PCRE2;前端镜像要求 Alpine PCRE2 至少 10.49-r0;环境 daemon 要求 PCRE2 至少 10.49-r0、Expat 至少 2.8.5-r0。Python、Node、pnpm、Go 与测试框架版本维持原样。 + +## 已执行验证 + +- 使用注册表返回的多架构摘要、子清单和各层 SHA-256 校验下载结果,检查两个架构的实际镜像配置与包数据库。 +- 对两个架构的原始 OCI 镜像执行 Grype 0.116.1,沿用 `.grype.yaml`、`--only-fixed --fail-on high`:High / Critical 均为 0。扫描对象未加入诊断工具。 +- 原始 arm64 镜像的临时诊断构建仅增加 BusyBox 及其加载器,在 UID / GID 65532 下验证 Nginx 配置及 HTTP 根路径:200。该构建未用于安全扫描,也不作为只读容器或完整 S26 验收的证明。 +- `make test-backend-targeted`:环境实验室、CI 计划与 Required Gate 的 131 项测试通过;`make test-frontend-targeted`:环境页面 3 项测试通过。 +- 受影响 Python 文件的 Ruff 格式与规则、前端文件的 Prettier / ESLint、全部工作流 YAML 解析及六处夹具引用一致性检查通过。 +- `pnpm --dir frontend build` 通过;`pip-audit` 无已知漏洞,`pnpm audit --audit-level high` 通过。后者保留测试框架已有的两个 moderate 报告,不在本次改动中升级工具链。 + +## 合并与验收边界 + +两处 CI 工作流仅修改镜像引用,不调整检查矩阵、漏洞阈值、忽略规则或正式状态发布器。该改动属于治理文件变更,必须按[受控 Bootstrap 流程](../development-efficiency-phase3.md#验证性能与激活)核对最新 Head 的完整远程检查及 Review Thread,再实施普通合并并恢复原门禁配置。具体远程运行与合并证据记录在对应 PR。 + +既有签名模板和项目允许列表不自动迁移;部署方仍需明确允许所选镜像。未执行生产部署、生产数据库迁移、Compact 容量 RC、真实外部服务或发布签字。本机普通 Docker 容器启动停留在 Created,已清理本次临时容器;完整的只读安全参数、Seed、重启后清理以 Bootstrap 的真实 Compose / S26 验收为准。原工作区及七个原有健康服务保持不变。 diff --git a/frontend/Dockerfile b/frontend/Dockerfile index de92a252..fa71cdc5 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -8,7 +8,7 @@ COPY . . RUN pnpm build FROM nginx:1.31.3-alpine-slim -RUN apk upgrade --no-cache libcrypto3 libssl3 +RUN apk add --no-cache --upgrade libcrypto3 libssl3 'pcre2>=10.49-r0' COPY nginx.conf /etc/nginx/conf.d/default.conf COPY --from=build /app/dist /usr/share/nginx/html diff --git a/frontend/package.json b/frontend/package.json index 75f91283..620df3eb 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -24,7 +24,7 @@ "@tanstack/react-query": "^5.101.4", "@xyflow/react": "^12.11.2", "antd": "^6.5.4", - "axios": "^1.13.5", + "axios": "^1.20.0", "echarts": "^6.0.0", "react": "^19.2.8", "react-dom": "^19.2.8", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 9c02c728..f2de2ae0 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -21,8 +21,8 @@ importers: specifier: ^6.5.4 version: 6.5.4(react-dom@19.2.8(react@19.2.8))(react@19.2.8) axios: - specifier: ^1.13.5 - version: 1.19.0(debug@4.4.3(supports-color@7.2.0))(supports-color@7.2.0) + specifier: ^1.20.0 + version: 1.20.0(debug@4.4.3(supports-color@7.2.0))(supports-color@7.2.0) echarts: specifier: ^6.0.0 version: 6.1.0 @@ -1061,8 +1061,8 @@ packages: asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - axios@1.19.0: - resolution: {integrity: sha512-ht/iuYZXEjFxLH/Hkezgd7m6JKlHHXEUSneaDz8uZe1Gj5QZtCnpyDsckvAiEnT89OEbCLmnte4R4sn7P0EKFw==} + axios@1.20.0: + resolution: {integrity: sha512-r8aOh8j9cGKpgQAqpzrUHnSIc6a59Y3Xf/cv8sy1DrHCkZHzQGEuoq1tARk6qSyDdtQGSDgpb9kFlruzPvrgwg==} balanced-match@4.0.4: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} @@ -1370,8 +1370,8 @@ packages: flatted@3.4.4: resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==} - follow-redirects@1.16.0: - resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} + follow-redirects@1.16.1: + resolution: {integrity: sha512-FNvFGzoMLWmE6Yj9spb/zjd7yiNCHiAW9/Tg9CXrQ8wuu32HtlJOwWO11OJafl5FfY3DxTdQ0vj42zU1kvv5jg==} engines: {node: '>=4.0'} peerDependencies: debug: '*' @@ -3289,9 +3289,9 @@ snapshots: asynckit@0.4.0: {} - axios@1.19.0(debug@4.4.3(supports-color@7.2.0))(supports-color@7.2.0): + axios@1.20.0(debug@4.4.3(supports-color@7.2.0))(supports-color@7.2.0): dependencies: - follow-redirects: 1.16.0(debug@4.4.3(supports-color@7.2.0)) + follow-redirects: 1.16.1(debug@4.4.3(supports-color@7.2.0)) form-data: 4.0.6 https-proxy-agent: 5.0.1(supports-color@7.2.0) proxy-from-env: 2.1.0 @@ -3595,7 +3595,7 @@ snapshots: flatted@3.4.4: {} - follow-redirects@1.16.0(debug@4.4.3(supports-color@7.2.0)): + follow-redirects@1.16.1(debug@4.4.3(supports-color@7.2.0)): optionalDependencies: debug: 4.4.3(supports-color@7.2.0) diff --git a/frontend/src/pages/EnvironmentLabPage.test.tsx b/frontend/src/pages/EnvironmentLabPage.test.tsx index 591dcc5c..1769091f 100644 --- a/frontend/src/pages/EnvironmentLabPage.test.tsx +++ b/frontend/src/pages/EnvironmentLabPage.test.tsx @@ -17,7 +17,7 @@ import { server } from '../test/server' import EnvironmentLabPage from './EnvironmentLabPage' const fixtureImage = - 'docker.io/nginxinc/nginx-unprivileged@sha256:123fb7283ffb4788e260d4e980005a978995fefedcdbb04d268077a19b84576d' + 'cgr.dev/chainguard/nginx@sha256:a104d1995e56b7a15e8f152078dfbdb1ecbf9f9d1af311e7906e7b4c0c790cf2' const template: EnvironmentTemplateVersion = { id: '00000000-0000-4000-8000-000000001001', diff --git a/frontend/src/pages/EnvironmentLabPage.tsx b/frontend/src/pages/EnvironmentLabPage.tsx index b42f30cf..4477779f 100644 --- a/frontend/src/pages/EnvironmentLabPage.tsx +++ b/frontend/src/pages/EnvironmentLabPage.tsx @@ -549,7 +549,7 @@ function TemplateDialog({ } const fixtureImage = - 'docker.io/nginxinc/nginx-unprivileged@sha256:123fb7283ffb4788e260d4e980005a978995fefedcdbb04d268077a19b84576d' + 'cgr.dev/chainguard/nginx@sha256:a104d1995e56b7a15e8f152078dfbdb1ecbf9f9d1af311e7906e7b4c0c790cf2' function templateFormValues(base?: EnvironmentTemplateVersion): TemplateForm { return base ? versionFormValues(base) : defaultTemplateFormValues diff --git a/mock-target/Dockerfile b/mock-target/Dockerfile index 75ebcf15..92dbb345 100644 --- a/mock-target/Dockerfile +++ b/mock-target/Dockerfile @@ -94,6 +94,16 @@ ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ PIP_NO_CACHE_DIR=1 +# Refresh fixed Debian libraries after the cached Python bootstrap. +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends --only-upgrade \ + libssl3t64 openssl-provider-legacy libpcre2-8-0; \ + dpkg --compare-versions "$(dpkg-query -W -f='${Version}' libssl3t64)" ge '3.5.7-1~deb13u3'; \ + dpkg --compare-versions "$(dpkg-query -W -f='${Version}' openssl-provider-legacy)" ge '3.5.7-1~deb13u3'; \ + dpkg --compare-versions "$(dpkg-query -W -f='${Version}' libpcre2-8-0)" ge '10.46-1~deb13u3'; \ + apt-get dist-clean + WORKDIR /app COPY --from=uv /uv /uvx /bin/ COPY pyproject.toml uv.lock ./ diff --git a/scripts/smoke_s26.py b/scripts/smoke_s26.py index d60e0b52..5f09d475 100755 --- a/scripts/smoke_s26.py +++ b/scripts/smoke_s26.py @@ -14,8 +14,8 @@ from smoke_s4 import APIClient, SmokeConfig, _change_password FIXTURE_IMAGE = ( - "docker.io/nginxinc/nginx-unprivileged@sha256:" - "123fb7283ffb4788e260d4e980005a978995fefedcdbb04d268077a19b84576d" + "cgr.dev/chainguard/nginx@sha256:" + "a104d1995e56b7a15e8f152078dfbdb1ecbf9f9d1af311e7906e7b4c0c790cf2" )