Browse Source

ci: add gitleaks secret scan + webui build job

让 GitHub CI 成为最后一道防线,避免再有 API key 滑进主分支。

Changes:
- secret-scan job (gates all other jobs): gitleaks-action with custom .github/gitleaks.toml
  covering 6 China-side providers (SiliconFlow / DashScope / Amap / DeepSeek / Zhipu /
  Moonshot) on top of gitleaks 默认规则集(OpenAI sk-* / GitHub token / AWS / etc.)。
  Allowlist 排除 .env.template, fixtures, tests, 占位符 (<your-key>, ${VAR})
- imports job (renamed from "lint"): 验证 lambdagent + agentpaas 包可 import,
  保留原有的 route 计数 smoke test
- webui job (NEW): Node 20 + npm ci + npm run build,
  catch broken frontend before merge
- test job: 加 pip cache + lambdagent src-layout 安装路径 (pip install -e lambdagent/)
- 所有非 secret-scan job 改为 needs: secret-scan,省 CI 分钟(key 泄漏直接全停)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
kenny67nju 3 months ago
parent
commit
dc02988bc2
2 changed files with 126 additions and 6 deletions
  1. 71 0
      .github/gitleaks.toml
  2. 55 6
      .github/workflows/ci.yml

+ 71 - 0
.github/gitleaks.toml

@@ -0,0 +1,71 @@
+# Gitleaks config — supplements the default ruleset with project-specific patterns.
+# Default rules already catch sk-* (OpenAI), ghp_*, AKIA*, etc.
+# Here we add China-side providers + project-specific allowlists.
+
+[extend]
+# Inherit gitleaks' built-in default rule set
+useDefault = true
+
+# ──────────────────────────────────────────────
+# Project-specific rules
+# ──────────────────────────────────────────────
+
+[[rules]]
+id = "siliconflow-api-key"
+description = "SiliconFlow API key (硅基流动) — sk-{40+ chars}"
+regex = '''sk-[a-z0-9]{40,}'''
+keywords = ["siliconflow", "SILICONFLOW_API_KEY"]
+
+[[rules]]
+id = "dashscope-api-key"
+description = "Aliyun DashScope (百炼) — sk-{32}"
+regex = '''sk-[a-f0-9]{32}'''
+keywords = ["dashscope", "DASHSCOPE_API_KEY", "bailian"]
+
+[[rules]]
+id = "amap-mcp-key"
+description = "Amap MCP key (高德地图) — 32-char hex"
+regex = '''(AMAP_MCP_KEY|amap.*key)['"\s:=]+[a-f0-9]{32}'''
+keywords = ["amap", "AMAP_MCP_KEY", "高德"]
+
+[[rules]]
+id = "deepseek-api-key"
+description = "DeepSeek API key — sk-{32}"
+regex = '''sk-[a-zA-Z0-9]{32}'''
+keywords = ["deepseek", "DEEPSEEK_API_KEY"]
+
+[[rules]]
+id = "zhipu-api-key"
+description = "Zhipu (GLM) API key"
+regex = '''[a-f0-9]{32}\.[a-zA-Z0-9]{16}'''
+keywords = ["zhipu", "ZHIPU_API_KEY", "智谱"]
+
+[[rules]]
+id = "moonshot-api-key"
+description = "Moonshot (Kimi) API key — sk-{48+}"
+regex = '''sk-[a-zA-Z0-9]{48,}'''
+keywords = ["moonshot", "MOONSHOT_API_KEY", "月之暗面"]
+
+# ──────────────────────────────────────────────
+# Allowlist (false positives + safe references)
+# ──────────────────────────────────────────────
+
+[allowlist]
+description = "Test fixtures, doc placeholders, and template files"
+paths = [
+  '''.*\.env\.template$''',
+  '''.*\.env\.example$''',
+  '''.*test.*\.(py|ts|js|yml)$''',
+  '''.*tests/.*''',
+  '''.*fixtures?/.*''',
+  '''.*/MEMORY\.md$''',  # session memory may include token-shaped strings as docs
+]
+regexes = [
+  '''<your-key>''',
+  '''<set-via-[A-Z_]+-env-var>''',
+  '''\$\{[A-Z_]+_API_KEY\}''',
+  '''YOUR_API_KEY''',
+  '''dummy[_-]?key''',
+  '''fake[_-]?key''',
+  '''sk-xxxx+''',
+]

+ 55 - 6
.github/workflows/ci.yml

@@ -7,9 +7,31 @@ on:
     branches: [main]
 
 jobs:
+  # ──────────────────────────────────────────────
+  # Secret leak scan — first line of defense
+  # 在跑任何其他东西之前先扫秘钥,省 CI 分钟
+  # ──────────────────────────────────────────────
+  secret-scan:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+        with:
+          fetch-depth: 0  # gitleaks 需要完整历史
+      - name: Run gitleaks
+        uses: gitleaks/gitleaks-action@v2
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          # GITLEAKS_LICENSE only needed for org-level; OSS repo runs free
+          GITLEAKS_CONFIG: .github/gitleaks.toml
+
+  # ──────────────────────────────────────────────
+  # Python tests on 3.10/3.11/3.12
+  # ──────────────────────────────────────────────
   test:
     runs-on: ubuntu-latest
+    needs: secret-scan
     strategy:
+      fail-fast: false
       matrix:
         python-version: ["3.10", "3.11", "3.12"]
     steps:
@@ -18,23 +40,50 @@ jobs:
         uses: actions/setup-python@v5
         with:
           python-version: ${{ matrix.python-version }}
-      - name: Install dependencies
+          cache: pip
+      - name: Install lambdagent (src-layout) + agentpaas
         run: |
-          pip install -e ".[dev]" 2>/dev/null || pip install pyyaml fastapi uvicorn pytest
+          pip install -e lambdagent/ 2>/dev/null || pip install pyyaml fastapi uvicorn pytest
+          pip install pytest pytest-asyncio pyyaml
       - name: Run tests
         run: python -m pytest tests/ -v --tb=short
-      - name: Lint check
+      - name: Lint module loads
         run: python -c "from lambdagent.fromconfig import lint_config; print('Lint module OK')"
 
-  lint:
+  # ──────────────────────────────────────────────
+  # Package import smoke test
+  # ──────────────────────────────────────────────
+  imports:
     runs-on: ubuntu-latest
+    needs: secret-scan
     steps:
       - uses: actions/checkout@v4
       - uses: actions/setup-python@v5
         with:
           python-version: "3.12"
-      - run: pip install pyyaml
-      - name: Check imports
+          cache: pip
+      - run: |
+          pip install -e lambdagent/ 2>/dev/null || pip install pyyaml fastapi uvicorn
+      - name: Verify package surface
         run: |
           python -c "import lambdagent; print(f'lambdagent OK: {len(lambdagent.__all__)} exports')"
           python -c "from agentpaas.api.app import app; print(f'AgentPaaS OK: {len([r for r in app.routes if hasattr(r, \"methods\")])} routes')"
+
+  # ──────────────────────────────────────────────
+  # Web UI build (catch broken frontend before merge)
+  # ──────────────────────────────────────────────
+  webui:
+    runs-on: ubuntu-latest
+    needs: secret-scan
+    defaults:
+      run:
+        working-directory: webui
+    steps:
+      - uses: actions/checkout@v4
+      - uses: actions/setup-node@v4
+        with:
+          node-version: "20"
+          cache: npm
+          cache-dependency-path: webui/package-lock.json
+      - run: npm ci
+      - run: npm run build