Workflows :
.gitea/workflows/tests.yml -> lint + unit + security (PR + push)
.gitea/workflows/security-audit.yml -> bandit + pip-audit + grep secrets
(hebdo + push main)
requirements-ci.txt : sous-ensemble léger de requirements.txt
- Sans torch, transformers, CUDA, FAISS binaire, Ollama, PyQt5, doctr
- Gain ~3 Go + ~2 min d'install CI
- À resynchroniser manuellement si nouveau test importe un package absent
Tests slow/gpu/integration/performance/visual/smoke exclus volontairement
(nécessitent CUDA, Ollama localhost:11434, serveur complet).
Temps estimé par run :
- Cold : ~3 min
- Warm (cache pip) : ~1m30
Security-tests (test_security_safe_condition + test_security_signed_serializer)
marqués bloquants : régression sur ast eval safe ou pickle HMAC casse la CI.
docs/CI_SETUP.md : activation Gitea Actions, enregistrement runner,
skip CI, troubleshooting.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
208 lines
7.0 KiB
YAML
208 lines
7.0 KiB
YAML
# ------------------------------------------------------------------
|
|
# 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
|