diff --git a/.gitea/workflows/ci-cd.yml b/.gitea/workflows/ci-cd.yml index 50c56a4f7..3e1306796 100644 --- a/.gitea/workflows/ci-cd.yml +++ b/.gitea/workflows/ci-cd.yml @@ -101,6 +101,53 @@ jobs: bandit --version pytest --version + - name: Secret detection (gitleaks) + shell: sh + run: | + set -eu + echo "=== Installing gitleaks ===" + # 优先尝试 GitHub release,失败则用国内镜像 + GITLEAKS_VERSION="8.18.4" + install_gitleaks() { + local url="$1" + curl -sSL -f -o /tmp/gitleaks.tar.gz "$url" || return 1 + tar -xzf /tmp/gitleaks.tar.gz -C /tmp gitleaks || return 1 + chmod +x /tmp/gitleaks || return 1 + /tmp/gitleaks version || return 1 + return 0 + } + if ! install_gitleaks "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz"; then + echo "GitHub release failed, trying mirror..." + if ! install_gitleaks "https://gitee.com/mirrors/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz"; then + echo "WARN: Failed to install gitleaks from all sources, skipping secret scan" + exit 0 + fi + fi + echo "" + echo "=== Running gitleaks scan ===" + if [ "${{ github.event_name }}" = "pull_request" ]; then + # PR触发: 增量扫描 + echo "PR mode: scanning changed files" + EXIT_CODE=0 + /tmp/gitleaks detect --source . --config .gitleaks.toml --verbose --exit-code 1 --log-opts="origin/${{ github.base_ref }}..HEAD" || EXIT_CODE=$? + if [ "$EXIT_CODE" = "1" ]; then + echo "ERROR: Secrets detected! Check the scan report above." + echo "If these are false positives, add them to .gitleaks.toml allowlist." + exit 1 + fi + else + # Push触发: 全量扫描 + echo "Push mode: full repository scan" + EXIT_CODE=0 + /tmp/gitleaks detect --source . --config .gitleaks.toml --verbose --exit-code 1 || EXIT_CODE=$? + if [ "$EXIT_CODE" = "1" ]; then + echo "ERROR: Secrets detected! Check the scan report above." + echo "If these are false positives, add them to .gitleaks.toml allowlist." + exit 1 + fi + fi + echo "gitleaks scan completed - no secrets detected" + - name: Run code quality checks shell: sh run: | @@ -110,12 +157,56 @@ jobs: python3 -m isort --check-only alembic apps packages tests scripts python3 -m flake8 apps packages tests --count --statistics - - name: Run security scan + - name: Run security scan (bandit) shell: sh run: | set -eu bandit -r apps packages -q -ll + - name: Python dependency vulnerability scan (pip-audit) + shell: sh + run: | + set -eu + echo "=== Installing pip-audit ===" + python3 -m pip install -q pip-audit + pip-audit --version + echo "" + echo "=== Scanning Python dependencies ===" + EXIT_CODE=0 + for req_file in requirements.txt requirements-base.txt requirements-dev.txt; do + if [ -f "$req_file" ]; then + echo "--- Scanning $req_file ---" + pip-audit -r "$req_file" --desc on --format text 2>&1 | head -30 || EXIT_CODE=$? + echo "" + fi + done + # 告警模式,不阻断CI(待稳定后再考虑改为阻断) + echo "pip-audit scan completed (advisory mode - warnings only, not blocking CI)" + if [ "$EXIT_CODE" != "0" ]; then + echo "WARNING: Potential vulnerabilities found in dependencies." + fi + exit 0 + + - name: Dead code detection (vulture) + shell: sh + run: | + set -eu + echo "=== Installing vulture ===" + python3 -m pip install -q vulture + vulture --version + echo "" + echo "=== Running vulture dead code scan ===" + # 告警模式,不阻断CI(P2级别,仅供参考) + EXIT_CODE=0 + vulture --config vulture.conf vulture_whitelist.py || EXIT_CODE=$? + echo "" + echo "vulture scan completed (advisory mode - P2, for reference only)" + if [ "$EXIT_CODE" != "0" ]; then + echo "NOTE: Potential dead code found. Review results above." + echo "False positives can be added to vulture_whitelist.py" + fi + exit 0 + - name: Validate release scripts syntax shell: sh run: | diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 000000000..41499d6cd --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,52 @@ +# .gitleaks.toml - gitleaks 白名单配置 +# 仓库: xiaoxia/xiaoxia-saas +# 用途: 排除已知的测试密钥、示例配置等误报 + +# 允许路径/文件排除 +[allowlist] +description = "全局白名单 - 排除示例配置和测试文件" +paths = [ + # 环境配置示例(无真实密钥) + '.env.example', + '.env.sample', + '*.env.example', + '*.env.sample', + # 测试文件 + 'tests/', + 'test/', + '*/tests/', + '*/test/', + # 文档 + 'docs/', + '*.md', + '*.rst', + # 前端依赖 + 'node_modules/', + # Python包 + 'site-packages/', + # 锁定文件(自动生成) + 'poetry.lock', + 'Pipfile.lock', + 'requirements*.txt.lock', + # CI配置本身 + '.gitea/', + # Docker相关 + 'docker-compose*.yml', + # gitleaks配置自身 + '.gitleaks.toml', +] + +# 允许的密钥值/占位符正则 +regexes = [ + # 占位符模式 + '''(?i)(your[_-]?password|your[_-]?secret|your[_-]?key|your[_-]?token|changeme|change[_-]?me|placeholder|example[_-]?key|test[_-]?key|dummy|fake|mock|xxx|none|not[_-]?set|TODO|FIXME)''', + # 数据库连接字符串中的通用密码(PostgreSQL示例配置) + '''postgresql://[^:]+:changeme@''', + '''postgresql://[^:]+:your-password@''', + '''postgresql://[^:]+:password@localhost''', + # Redis示例配置 + '''redis://:changeme@''', + '''redis://:your-redis-password@''', + # JWT示例密钥 + '''(?i)jwt[_-]?secret\s*[:=]\s*["']?(your[_-]?jwt|change|placeholder|secret|example)''', +] diff --git a/vulture.conf b/vulture.conf new file mode 100644 index 000000000..22ba44643 --- /dev/null +++ b/vulture.conf @@ -0,0 +1,35 @@ +# vulture.conf - 死代码检测配置 +# 仓库: xiaoxia/xiaoxia-saas +# 用途: 检测未使用的函数、变量、导入、类、方法、属性 + +# 扫描目录(空格分隔) +path = alembic apps packages scripts + +# 排除路径(每个路径一行,相对于仓库根目录) +exclude = + tests + test + */tests + */test + site-packages + node_modules + migrations + .gitea + docs + scripts/check_*.py + scripts/init_*.py + +# 最低置信度 (%) +# 0 = 报告所有可能的未使用代码 +# 100 = 只报告确定未使用的代码 +# 推荐从 80% 开始,逐步调高 +min-confidence = 80 + +# 输出格式: string, json, yaml +format = text + +# 按置信度排序 +sort-by-size = False + +# 显示置信度 +show-uncertain = True diff --git a/vulture_whitelist.py b/vulture_whitelist.py new file mode 100644 index 000000000..155f481b7 --- /dev/null +++ b/vulture_whitelist.py @@ -0,0 +1,57 @@ +# vulture_whitelist.py - vulture 白名单文件 +# 用途: 列出已知被框架/动态调用的代码,避免误报 +# 参考: https://vulture.readthedocs.io/en/stable/whitelists.html + +# FastAPI / Starlette 框架自动调用 +# FastAPI route handlers (通过装饰器注册,vulture 可能无法识别) +apps.*.main.* +apps.*.api.* +apps.*.routes.* +apps.*.views.* + +# SQLAlchemy ORM +# Model 类和字段通过 ORM 框架自动使用 +apps.*.models.* +apps.*.schemas.* +packages.*.models.* + +# Pydantic models +# Pydantic 字段通过序列化/反序列化使用 +apps.*.schemas.* +packages.*.schemas.* + +# Alembic migrations +# Migration 函数由 alembic 自动调用 +alembic.versions.*.upgrade +alembic.versions.*.downgrade + +# Celery tasks +# Task 函数通过 celery worker 调用 +apps.*.tasks.* +packages.*.tasks.* + +# CLI scripts / entry points +# 脚本通过命令行调用 +scripts.* + +# 中间件 +apps.*.middleware.* +packages.*.middleware.* + +# 异常类 +apps.*.exceptions.* +packages.*.exceptions.* + +# 配置类 +apps.*.config.* +packages.*.config.* + +# 工具函数(可能被多处间接调用,先白名单,后续清理) +apps.*.utils.* +packages.*.utils.* +apps.*.helpers.* +packages.*.helpers.* + +# Dependencies (FastAPI Depends) +apps.*.dependencies.* +packages.*.dependencies.*