From f9f0ebf15a1727c983ffec40a3cd16ab02de56e8 Mon Sep 17 00:00:00 2001 From: Delicious233 <101502465+DeliciousBuding@users.noreply.github.com> Date: Mon, 17 Aug 2026 17:38:34 +0800 Subject: [PATCH] =?UTF-8?q?ci(security):=20=E6=8E=A5=E7=BA=BF=20gosec=20?= =?UTF-8?q?=E8=B4=9F=E5=90=91=E8=87=AA=E6=B5=8B=E5=B9=B6=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=20check-secrets=20=E8=B4=9F=E5=90=91=E8=87=AA=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - validate job 补 Self-test gosec gate 与 Self-test secret guard 两步 - 新增 check-secrets.Tests.sh:7 用例覆盖 placeholder/URL 放行与 AKIA/ghp_/sk-/private key 拒收 - verify-ci-gates.py 断言两步存在,防止未来漂移 Co-authored-by: Cursor --- .github/workflows/checks.yml | 8 ++ scripts/verify/tests/check-secrets.Tests.sh | 104 ++++++++++++++++++++ scripts/verify/verify-ci-gates.py | 2 + 3 files changed, 114 insertions(+) create mode 100644 scripts/verify/tests/check-secrets.Tests.sh diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 88ae94031..5d993634a 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -1301,6 +1301,14 @@ jobs: shell: bash run: bash scripts/verify/tests/verify-vulnerability-gates.Tests.sh + - name: Self-test gosec gate (fail-closed contract) + shell: bash + run: bash scripts/verify/tests/verify-gosec-gates.Tests.sh + + - name: Self-test secret guard (fail-closed contract) + shell: bash + run: bash scripts/verify/tests/check-secrets.Tests.sh + - name: Verify Web Hub-only boundary run: python scripts/verify/verify-web-hub-boundary.py diff --git a/scripts/verify/tests/check-secrets.Tests.sh b/scripts/verify/tests/check-secrets.Tests.sh new file mode 100644 index 000000000..84808a8d9 --- /dev/null +++ b/scripts/verify/tests/check-secrets.Tests.sh @@ -0,0 +1,104 @@ +#!/usr/bin/env bash +# Self-tests for check-secrets.sh — fail-closed secret guard contract. +# +# Positive: placeholder values, *.env.example files, and *_URL endpoint +# assignments pass. +# Negative: real AWS keys, GitHub tokens, private key blocks, and API keys +# (sk-...) in staged diffs must ALL exit non-zero. +# +# Runs in CI validate job alongside verify-vulnerability-gates.Tests.sh. +set -uo pipefail + +SCRIPT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/check-secrets.sh" +PASS=0 +FAIL=0 + +check() { + local name="$1" expect_fail="$2" actual="$3" + if { [[ "$expect_fail" == "yes" ]] && [[ "$actual" -ne 0 ]]; } || \ + { [[ "$expect_fail" == "no" ]] && [[ "$actual" -eq 0 ]]; }; then + echo " PASS $name (exit=$actual)" + PASS=$((PASS + 1)) + else + echo " FAIL $name (exit=$actual, expected $([[ "$expect_fail" == "yes" ]] && echo "!=0" || echo "0"))" + FAIL=$((FAIL + 1)) + fi +} + +# Create a temp git repo so check-secrets.sh has a git root to scan. +TMP_REPO="$(mktemp -d)" +trap 'rm -rf "$TMP_REPO"' EXIT +cd "$TMP_REPO" +git init --quiet +git config user.email "test@example.com" +git config user.name "Test" + +echo "=== empty worktree (no changes) ===" +bash "$SCRIPT" --worktree >/dev/null 2>&1 +check "empty worktree exits 0" no $? + +echo "=== placeholder .env.example passes ===" +cat > .env.example <<'ENV' +AGENTHUB_TOKENDANCE_ID_CLIENT_SECRET=your-client-secret-here +AGENTHUB_HUB_JWT_SECRET=change-me-in-production +ENV +git add .env.example +bash "$SCRIPT" --staged >/dev/null 2>&1 +check "placeholder .env.example passes" no $? + +echo "=== *_URL endpoint assignment passes ===" +cat > config.yaml <<'YAML' +AGENTHUB_TOKENDANCE_ID_ISSUER_URL=https://id.example.com +AGENTHUB_TOKENDANCE_ID_REDIRECT_URI=https://hub.example.com/client/auth/callback +YAML +git add config.yaml +git commit --quiet -m "add config" 2>/dev/null +# Now stage a new change with a URL assignment +echo 'AGENTHUB_WEB_URL=https://web.example.com' >> config.yaml +git add config.yaml +bash "$SCRIPT" --staged >/dev/null 2>&1 +check "*_URL endpoint assignment passes" no $? + +echo "=== real AWS access key fails ===" +git reset --quiet +cat > secrets.yaml <<'YAML' +aws_access_key_id: AKIAIOSFODNN7EXAMPLE +YAML +git add secrets.yaml +bash "$SCRIPT" --staged >/dev/null 2>&1 +check "AWS access key fails" yes $? + +echo "=== GitHub token fails ===" +git reset --quiet +cat > gh-token.json <<'JSON' +{"token": "ghp_1234567890abcdefghijklmnopqrstuvwxyzABCD"} +JSON +git add gh-token.json +bash "$SCRIPT" --staged >/dev/null 2>&1 +check "GitHub token fails" yes $? + +echo "=== private key block fails ===" +git reset --quiet +cat > key.pem <<'PEM' +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA1234567890abcdefghijklmnopqrstuvwxyz +-----END RSA PRIVATE KEY----- +PEM +git add key.pem +bash "$SCRIPT" --staged >/dev/null 2>&1 +check "private key file path fails" yes $? + +echo "=== API key (sk-) fails ===" +git reset --quiet +cat > api.yaml <<'YAML' +openai_api_key: sk-proj-1234567890abcdefghijklmnopqrstuvwxyz +YAML +git add api.yaml +bash "$SCRIPT" --staged >/dev/null 2>&1 +check "sk- API key fails" yes $? + +echo "" +echo "Results: $PASS passed, $FAIL failed" +if [[ "$FAIL" -gt 0 ]]; then + exit 1 +fi diff --git a/scripts/verify/verify-ci-gates.py b/scripts/verify/verify-ci-gates.py index ffcebef59..bdc9cf359 100644 --- a/scripts/verify/verify-ci-gates.py +++ b/scripts/verify/verify-ci-gates.py @@ -132,6 +132,8 @@ def main() -> int: assert_contains(vuln_go, re.escape("verify-vulnerability-gates.sh govulncheck"), "vuln-scan-go must run the fail-closed govulncheck verifier") assert_contains(vuln_js, re.escape("verify-vulnerability-gates.sh pnpm-audit"), "vuln-scan-js must run the fail-closed pnpm audit verifier") assert_contains(validate, r"Self-test vulnerability gates", "validate must self-test the vulnerability gates") + assert_contains(validate, r"Self-test gosec gate", "validate must self-test the gosec fail-closed contract") + assert_contains(validate, r"Self-test secret guard", "validate must self-test the secret guard fail-closed contract") # Native Windows is a compatibility contract, not a duplicate release # pipeline: backend and frontend matrix legs must remain path-filtered and