fix(devops): .env.example 移除可用 Fernet key 换不可用占位符 + 轮换提示(CR-H8)
This commit is contained in:
@@ -6,6 +6,7 @@ DATABASE_URL_SYNC=postgresql+psycopg://writer:writer@localhost:5432/writer
|
|||||||
APP_ENV=dev
|
APP_ENV=dev
|
||||||
LOG_JSON=false
|
LOG_JSON=false
|
||||||
# 凭据加密密钥(Fernet, urlsafe-base64 32B)— **必填**:API 启动即校验,缺失/非法则拒绝启动。
|
# 凭据加密密钥(Fernet, urlsafe-base64 32B)— **必填**:API 启动即校验,缺失/非法则拒绝启动。
|
||||||
# 下方为开发占位 key(可直接用于本地);生产请换新值:
|
# ⚠️ 下方为**不可用占位符**,必须替换:`cp .env.example .env` 后用下方命令生成真实 key 填入。
|
||||||
|
# 切勿把真实 key 提交进版本库;任何曾经泄露过的 key 必须立即轮换(重新加密已存凭据)。
|
||||||
# uv run python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
|
# uv run python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
|
||||||
CREDENTIAL_ENC_KEY=cnMpG6QQxJejuDLHTe_S-nq2snoKgXCqWFfsctEHB-4=
|
CREDENTIAL_ENC_KEY=replace-me-with-generated-fernet-key
|
||||||
|
|||||||
38
tests/test_env_example_no_secret.py
Normal file
38
tests/test_env_example_no_secret.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
"""回归守卫:`.env.example` 不得携带可用的真实密钥(CR-H8)。
|
||||||
|
|
||||||
|
`.env.example` 是入库模板;若其中的 `CREDENTIAL_ENC_KEY` 是一把真实可用的
|
||||||
|
Fernet key,任何照抄 `.env.example` 的环境都会共用同一把泄露过的密钥。此测试
|
||||||
|
确保模板里只放**不可用占位符**——照抄到 `.env` 会在启动校验(main._lifespan)
|
||||||
|
处快速失败,逼迫运维生成真实 key。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import re
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from cryptography.fernet import Fernet
|
||||||
|
|
||||||
|
_ENV_EXAMPLE = Path(__file__).resolve().parent.parent / ".env.example"
|
||||||
|
_PLACEHOLDER = "replace-me-with-generated-fernet-key"
|
||||||
|
|
||||||
|
|
||||||
|
def _credential_enc_key() -> str:
|
||||||
|
text = _ENV_EXAMPLE.read_text(encoding="utf-8")
|
||||||
|
match = re.search(r"^CREDENTIAL_ENC_KEY=(.*)$", text, flags=re.MULTILINE)
|
||||||
|
assert match is not None, ".env.example 缺少 CREDENTIAL_ENC_KEY 行"
|
||||||
|
return match.group(1).strip()
|
||||||
|
|
||||||
|
|
||||||
|
def test_env_example_credential_key_is_non_functional_placeholder() -> None:
|
||||||
|
value = _credential_enc_key()
|
||||||
|
assert value, "CREDENTIAL_ENC_KEY 不能为空"
|
||||||
|
# 真实可用的 Fernet key 会构造成功——那正是要杜绝的泄露面。
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
Fernet(value.encode())
|
||||||
|
|
||||||
|
|
||||||
|
def test_env_example_credential_key_is_the_expected_placeholder() -> None:
|
||||||
|
# 更紧的锁:值必须恰是约定占位符,防止未来又塞进别的真实 key。
|
||||||
|
assert _credential_enc_key() == _PLACEHOLDER
|
||||||
Reference in New Issue
Block a user