Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/operator-guide.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,11 @@ image;项目安全设置只能收紧用户限额和默认禁止路径,不能
未跟踪文件、符号链接、`.env` 文件、仓库根级 `secrets`/`credentials` 前缀和包含通配符或父目录
跳转的路径仍会失败关闭。每个授权前缀都会写入验证沙箱清单,便于 Review 时核对。

已跟踪的 `.env.example`、`.env.sample` 和 `.env.template` 可以进入验证快照,但敏感字段只允许
空值或严格的占位符:`replace-with-<名称>`、`your-<提供方>-api-key`。名称和提供方仅允许小写
ASCII 字母、数字及分隔非空片段的连字符;整个占位符不能加引号且最多 128 字符。模板仍须满足
文件大小、UTF-8、普通文件、目录和赋值语法检查;其他非空敏感值继续拒绝,错误只报告位置。

### 并发与变更规模

默认情况下,每个仓库最多同时保留 4 个由当前 GitHub 账号创建的 open PR;Draft 和 Ready 都计入,
Expand Down
5 changes: 4 additions & 1 deletion src/reposteward/verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@
re.IGNORECASE,
)
EMPTY_ENV_VALUE = re.compile(r"(?:|''|\"\")(?:\s+#.*)?")
SAFE_ENV_PLACEHOLDER = re.compile(r"replace-with-[a-z0-9]+(?:-[a-z0-9]+)*")
SAFE_ENV_PLACEHOLDER = re.compile(
r"(?:replace-with-[a-z0-9]+(?:-[a-z0-9]+)*"
r"|your-[a-z0-9]+(?:-[a-z0-9]+)*-api-key)"
)


class VerificationError(RuntimeError):
Expand Down
43 changes: 43 additions & 0 deletions tests/test_verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,37 @@ def test_tracked_environment_template_accepts_explicit_placeholders(self) -> Non
template.read_text(encoding="utf-8"),
)

def test_tracked_template_accepts_api_key_placeholders(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
worktree = root / "worktree"
worktree.mkdir()
_repository(worktree)
values = (
"your-serper-api-key",
"your-serply-api-key",
"your-tavily-api-key",
"your-jina-api-key",
"your-infoquest-api-key",
"your-sofya-api-key",
"your-provider-v2-api-key",
"your-" + "a" * 115 + "-api-key",
)
content = "".join(
f"PROVIDER_{index}_API_KEY={value}\n"
for index, value in enumerate(values)
)
template = worktree / ".env.example"
template.write_text(content, encoding="utf-8")
subprocess.run(["git", "add", ".env.example"], cwd=worktree, check=True)

DockerVerifier._copy_workspace(worktree, root / "snapshot")

self.assertEqual(
(root / "snapshot" / ".env.example").read_bytes(),
template.read_bytes(),
)

def test_untracked_environment_template_is_excluded(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
Expand Down Expand Up @@ -414,6 +445,18 @@ def test_environment_template_rejects_invalid_sensitive_placeholders(self) -> No
"replace-with-token.value",
'"replace-with-a-token"',
"replace-with-" + "a" * 129,
"your-api-key",
"your--api-key",
"your-UPPER-api-key",
"your-provider_api-key",
"your-provider--v2-api-key",
"your-provider-api-key.value",
"your-provider-api-key-extra",
'"your-provider-api-key"',
"'your-provider-api-key'",
"your-provider-api-key$(id)",
"your-供应商-api-key",
"your-" + "a" * 116 + "-api-key",
)
):
with self.subTest(value=value):
Expand Down