From e9ccffe82567e4f926cbe2dea6b20752f4d3ee23 Mon Sep 17 00:00:00 2001 From: Bamboo <13664854532@163.com> Date: Sat, 24 Oct 2026 11:21:07 +0800 Subject: [PATCH] fix(ci): establish reviewed scan policy and release JWT issuer --- .gitleaks.toml | 26 ++++++++++++++++++++++++++ deploy/release/compose.yaml | 1 + scripts/tests/test_release_env.py | 23 +++++++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 .gitleaks.toml create mode 100644 scripts/tests/test_release_env.py diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 00000000..510f171b --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,26 @@ +# gitleaks 配置(SEC-01A):在官方默认规则基础上扩展。 +# 规则:只对“确定的误报”(公开样例值/测试夹具/占位符)做窄范围 allowlist,每条都必须写明原因; +# 绝不对真实密钥做 allowlist —— 真实密钥必须移除并轮换(见 SEC-01B runbook)。 +# 本仓库的钩子与 CI 均带 --ignore-gitleaks-allow:源码中的 "gitleaks:allow" 行内注释不再豁免任何发现, +# 误报只能在本文件按值精确放行(CI 使用 base 分支上的本文件,改动须经评审合入后才生效)。 +title = "AI-CloudOps backend gitleaks config" + +[extend] +useDefault = true + +# RFC 6455 第 1.3 节公开示例 Sec-WebSocket-Key("the sample nonce" 的 base64),不是凭据。 +# 按“值 + 文件 + 规则”同时满足才放行(condition = "AND"): +# - 同一文件同一行换成其他任何值(如真实密钥)仍会被报; +# - 同一个值出现在其他文件也仍会被报。 +# 取代原先 .gitleaksignore 中按 path:rule:line / commit:path:rule:line 的指纹(行号一变就失效、且按行放行任意值)。 +# sched/w1-mantou(8bb2307)已改为运行时构造该值,HEAD 中不再出现该值;但历史仍含该值: +# gitleaks 8.30.1 + 默认规则 + --ignore-gitleaks-allow 实测,24b8315 两处(login_test.go 106/127 行)、 +# 125bc27 两处(w1-mantou 上,109/130 行),合计 4 处;用本文件扫描时为 0(2026-10-24 review3g)。 +# 因此只要还会用 HEAD 配置对包含这些提交的历史做全量扫描,本条就必须保留,不能删除。 +[[allowlists]] +description = "RFC 6455 sample Sec-WebSocket-Key nonce in login_test.go" +targetRules = ["generic-api-key"] +condition = "AND" +paths = ['''^internal/middleware/login_test\.go$'''] +regexTarget = "secret" +regexes = ['''^dGhlIHNhbXBsZSBub25jZQ==$'''] diff --git a/deploy/release/compose.yaml b/deploy/release/compose.yaml index d575393a..cd7f54b0 100644 --- a/deploy/release/compose.yaml +++ b/deploy/release/compose.yaml @@ -75,6 +75,7 @@ services: REDIS_PASSWORD: ${REDIS_PASSWORD:?Generate .env first} JWT_KEY1: ${JWT_KEY1:?Generate .env first} JWT_KEY2: ${JWT_KEY2:?Generate .env first} + JWT_ISSUER: ${JWT_ISSUER:-K5mBPBYNQeNWEBvCTE5msog3KSGTdhmx} JWT_EXPIRATION: "30" TREE_PASSWORD_ENCRYPTION_KEY: ${TREE_PASSWORD_ENCRYPTION_KEY:?Generate .env first} BOOTSTRAP_ADMIN_PASSWORD: ${BOOTSTRAP_ADMIN_PASSWORD:?Generate .env first} diff --git a/scripts/tests/test_release_env.py b/scripts/tests/test_release_env.py new file mode 100644 index 00000000..8096d498 --- /dev/null +++ b/scripts/tests/test_release_env.py @@ -0,0 +1,23 @@ +"""Release container configuration regression without Docker or external services.""" + +from pathlib import Path +import re +import unittest + +ROOT = Path(__file__).resolve().parents[2] +COMPOSE = ROOT / "deploy/release/compose.yaml" + + +class ReleaseEnvTests(unittest.TestCase): + def test_release_container_issuer_matches_repository_configuration(self): + # The runtime image only copies the binary: a YAML-only issuer would stop startup. + compose = COMPOSE.read_text() + match = re.search(r"^\s+JWT_ISSUER:\s*\$\{JWT_ISSUER:-([^}]+)\}\s*$", compose, re.M) + self.assertIsNotNone(match, "release backend must receive a nonempty JWT_ISSUER") + for name in ("config.production.yaml", "config.development.yaml"): + configured = re.search(r'^\s+issuer:\s*"([^"\n]+)"\s*$', + (ROOT / "config" / name).read_text(), re.M) + self.assertIsNotNone(configured) + self.assertEqual(match.group(1), configured.group(1), + "changing issuer on container upgrade would invalidate existing tokens") +