fix: reject compose env selector aliases
This commit is contained in:
@@ -23,8 +23,10 @@ require_selector() {
|
||||
local key="$1"
|
||||
local expected="$2"
|
||||
local count
|
||||
local definition_pattern
|
||||
|
||||
count="$(grep -c "^${key}=" "$ENV_FILE" || true)"
|
||||
definition_pattern="^[[:space:]]*(export[[:space:]]+)?${key}([[:space:]]*=|[[:space:]]*$)"
|
||||
count="$(grep -Ec "$definition_pattern" "$ENV_FILE" || true)"
|
||||
if [ "$count" -ne 1 ] || ! grep -Fqx "${key}=${expected}" "$ENV_FILE"; then
|
||||
echo "invalid staging selector: $key" >&2
|
||||
exit 1
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
| 部署密钥 | production 专用 | staging 专用 |
|
||||
| 应用配置 | `.env.production` | `.env.staging` |
|
||||
| Supabase | 生产项目 | 独立 staging 项目 |
|
||||
| 部署触发 | `main` CI 成功 | `staging` CI 成功或手动触发 |
|
||||
| 部署触发 | 手动 production workflow | `staging` CI 成功或手动触发 |
|
||||
|
||||
staging 不得写入生产数据库,不得复用 service-role key、数据库密码、SSH 私钥或模型计费密钥。模型接口优先使用独立测试 key、低额度或 provider sandbox。
|
||||
|
||||
|
||||
@@ -182,6 +182,7 @@ test("staging env validator rejects selector drift, duplicates, and unsafe permi
|
||||
);
|
||||
const root = mkdtempSync(join(tmpdir(), "jyotisha-staging-env-"));
|
||||
const envFile = join(root, ".env.staging");
|
||||
const composeFile = join(root, "compose.yml");
|
||||
const validSelectors = [
|
||||
"APP_ENV_FILE=../.env.staging",
|
||||
"CADDYFILE_PATH=./Caddyfile.staging",
|
||||
@@ -195,6 +196,17 @@ test("staging env validator rejects selector drift, duplicates, and unsafe permi
|
||||
};
|
||||
|
||||
try {
|
||||
writeFileSync(
|
||||
composeFile,
|
||||
[
|
||||
"services:",
|
||||
" probe:",
|
||||
" image: alpine",
|
||||
" environment:",
|
||||
" SELECTED: ${APP_ENV_FILE}",
|
||||
"",
|
||||
].join("\n"),
|
||||
);
|
||||
writeEnv(validSelectors);
|
||||
assert.equal(run().status, 0);
|
||||
|
||||
@@ -204,6 +216,34 @@ test("staging env validator rejects selector drift, duplicates, and unsafe permi
|
||||
writeEnv([...validSelectors, "SITE_ADDRESS=https://example.invalid"]);
|
||||
assert.notEqual(run().status, 0);
|
||||
|
||||
writeEnv([...validSelectors, "APP_ENV_FILE = ../.env.production"]);
|
||||
assert.notEqual(run().status, 0);
|
||||
const rendered = spawnSync(
|
||||
"docker",
|
||||
[
|
||||
"compose",
|
||||
"--env-file",
|
||||
envFile,
|
||||
"-f",
|
||||
composeFile,
|
||||
"config",
|
||||
"--format",
|
||||
"json",
|
||||
],
|
||||
{ encoding: "utf8" },
|
||||
);
|
||||
assert.equal(rendered.status, 0, rendered.stderr);
|
||||
assert.equal(
|
||||
JSON.parse(rendered.stdout).services.probe.environment.SELECTED,
|
||||
"../.env.production",
|
||||
);
|
||||
|
||||
writeEnv([...validSelectors, "export CADDYFILE_PATH=./Caddyfile"]);
|
||||
assert.notEqual(run().status, 0);
|
||||
|
||||
writeEnv([...validSelectors, "SITE_ADDRESS"]);
|
||||
assert.notEqual(run().status, 0);
|
||||
|
||||
writeEnv(validSelectors, 0o644);
|
||||
assert.notEqual(run().status, 0);
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user