# ------------------------------------------------------------------ # Audit sécurité — bandit + pip-audit + scan secrets # ------------------------------------------------------------------ # Jamais bloquant : on reporte les warnings, on ne casse pas la CI. # Utile pour détecter les dérives progressives (nouveaux CVE, secrets # oubliés dans un commit, patterns risqués). # # Fréquence : à chaque push sur main + hebdo (cron). # ------------------------------------------------------------------ name: security-audit on: push: branches: - main schedule: # Tous les lundis à 6h UTC (8h Paris hiver, 7h Paris été). - cron: "0 6 * * 1" workflow_dispatch: {} concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: # ---------------------------------------------------------------- # Job 1 — bandit (bonnes pratiques sécu Python) # ---------------------------------------------------------------- bandit: name: Bandit (scan statique) runs-on: ubuntu-latest timeout-minutes: 5 continue-on-error: true steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Python 3.12 uses: actions/setup-python@v5 with: python-version: "3.12" cache: "pip" - name: Installation bandit run: | python -m pip install --upgrade pip pip install "bandit[toml]==1.7.10" - name: Scan bandit sur core/ run: | # -ll : niveau LOW minimum (remonte tout) # -ii : confiance LOW minimum # --skip B101 : on ignore les asserts (usuels en tests/validation) bandit -r core/ \ --skip B101,B404,B603 \ --format txt \ --exit-zero \ --output bandit-report.txt echo "=== RAPPORT BANDIT ===" cat bandit-report.txt - name: Upload rapport bandit if: always() uses: actions/upload-artifact@v3 with: name: bandit-report path: bandit-report.txt retention-days: 30 if-no-files-found: ignore # ---------------------------------------------------------------- # Job 2 — pip-audit (CVE sur requirements) # ---------------------------------------------------------------- pip-audit: name: pip-audit (CVE dépendances) runs-on: ubuntu-latest timeout-minutes: 5 continue-on-error: true steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Python 3.12 uses: actions/setup-python@v5 with: python-version: "3.12" cache: "pip" - name: Installation pip-audit run: | python -m pip install --upgrade pip pip install "pip-audit==2.7.3" - name: Audit CVE sur requirements-ci.txt run: | if [ -f requirements-ci.txt ]; then pip-audit -r requirements-ci.txt \ --format json \ --output pip-audit-ci.json \ --progress-spinner off \ --disable-pip || echo "::warning::CVE détectées dans requirements-ci.txt" echo "=== RAPPORT pip-audit (CI) ===" cat pip-audit-ci.json || true else echo "::notice::requirements-ci.txt absent — skip" fi - name: Audit CVE sur requirements.txt (best-effort) run: | # Timeout généreux car requirements.txt est massif (torch, CUDA). timeout 120 pip-audit -r requirements.txt \ --format json \ --output pip-audit-full.json \ --progress-spinner off \ --disable-pip 2>&1 | head -200 || \ echo "::warning::pip-audit sur requirements.txt a timeout ou échoué (non bloquant)" - name: Upload rapports pip-audit if: always() uses: actions/upload-artifact@v3 with: name: pip-audit-reports path: | pip-audit-ci.json pip-audit-full.json retention-days: 30 if-no-files-found: ignore # ---------------------------------------------------------------- # Job 3 — Scan secrets en clair (grep simple) # ---------------------------------------------------------------- # Patterns recherchés : clés API Anthropic (sk-ant-), OpenAI (sk-), # Google (AIzaSy), AWS (AKIA), tokens Hugging Face (hf_). # Ne cherche QUE dans les fichiers trackés (pas .env, pas .venv). # ---------------------------------------------------------------- secrets-scan: name: Scan secrets (grep) runs-on: ubuntu-latest timeout-minutes: 3 continue-on-error: true steps: - name: Checkout (historique complet) uses: actions/checkout@v4 with: fetch-depth: 0 - name: Scan patterns de secrets run: | # Chemins exclus : venvs, caches, data, htmlcov, models. EXCLUDES='--exclude-dir=.venv --exclude-dir=venv_v3 --exclude-dir=.git \ --exclude-dir=node_modules --exclude-dir=htmlcov --exclude-dir=models \ --exclude-dir=data --exclude-dir=__pycache__ --exclude-dir=.pytest_cache \ --exclude=*.lock --exclude=*.log --exclude=*.md' echo "=== Recherche de secrets potentiels ===" FOUND=0 # Anthropic if grep -rnI $EXCLUDES -E 'sk-ant-[a-zA-Z0-9_-]{20,}' . 2>/dev/null; then echo "::warning::Clé Anthropic potentielle détectée" FOUND=1 fi # OpenAI if grep -rnI $EXCLUDES -E 'sk-proj-[a-zA-Z0-9_-]{20,}|sk-[a-zA-Z0-9]{40,}' . 2>/dev/null; then echo "::warning::Clé OpenAI potentielle détectée" FOUND=1 fi # Google Cloud / API Keys if grep -rnI $EXCLUDES -E 'AIzaSy[a-zA-Z0-9_-]{33}' . 2>/dev/null; then echo "::warning::Clé Google API potentielle détectée" FOUND=1 fi # AWS if grep -rnI $EXCLUDES -E 'AKIA[0-9A-Z]{16}' . 2>/dev/null; then echo "::warning::Clé AWS potentielle détectée" FOUND=1 fi # Hugging Face if grep -rnI $EXCLUDES -E 'hf_[a-zA-Z0-9]{30,}' . 2>/dev/null; then echo "::warning::Token Hugging Face potentiel détecté" FOUND=1 fi # Mots-clés suspects à côté d'assignations if grep -rnI $EXCLUDES -E '(password|passwd|secret|api_key|apikey|token)\s*=\s*["\x27][a-zA-Z0-9_\-!@#\$%]{12,}["\x27]' . 2>/dev/null \ | grep -viE '(example|dummy|placeholder|test|fake|xxx|changeme|\$\{)' 2>/dev/null; then echo "::warning::Assignation suspecte d'un secret détectée" FOUND=1 fi if [ "$FOUND" -eq 0 ]; then echo "Aucun secret détecté par les patterns de base." else echo "" echo "::notice::Vérifier manuellement les occurrences ci-dessus." echo "::notice::Si faux positif : ajouter le fichier aux exclusions ou reformater." fi # Toujours succès (job non bloquant). exit 0