CI/CD Integration
Guides
Intégration CI/CD
Chaque endpoint de scanner accepte une clé API, si bien que chacun peut s'exécuter comme une étape de pipeline. Cette page explique ce que signifie la sortie de chaque outil, comment la transformer en gate réussite/échec, et fournit des exemples fonctionnels pour quatre plateformes CI.
Analyse de code avec la GitHub Action
Le moyen le plus rapide d'analyser le code en CI. Elle exécute @scanverra/cli sur le runner (votre code ne le quitte jamais), fait échouer le job à partir de la gravité choisie et envoie le SARIF à GitHub code scanning.
on: [push, pull_request]
jobs:
scanverra:
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@v4
- uses: scanverra/scan-action@v1
with:
api-key: ${{ secrets.SCANVERRA_API_KEY }}
fail-on: highBase de notation par outil
Ce que renvoie chaque endpoint, et sur quoi fixer un seuil.
| Outil | Sortie | Base | Gate intégré |
|---|---|---|---|
| Audit de site web | 4 scores 0-100 : performance, seo, accessibility, bestPractices | Scores par catégorie de 0 à 100. Convention : ≥90 bon, 50-89 à améliorer, <50 mauvais. | Aucun gate intégré — choisissez votre propre seuil par score. |
| Analyse de sécurité | riskScore 0-100 (plus élevé = plus sûr) | 100 moins des déductions pondérées selon la gravité, plafonnées par catégorie. Tranches de notation : A+ ≥90 … F <30. | Aucun gate intégré — choisissez votre propre seuil. |
| Audit navigateur | Tableaux : jsErrors, brokenLinks, formIssues, imgIssues (chacun avec severity: "error" | "warning") | Aucun score global. | Conditionnez sur le nombre de problèmes, par exemple toute severity === "error". |
| Analyseur de dépôts | score 0-100 + qualityGate: "passed" | "failed" | 100 moins des déductions pondérées selon la gravité, plafonnées par catégorie. | Intégré — échoue automatiquement en cas de résultat critique ou de score < 60. |
| Optimiseur SEO | score 0-100 (SEO on-page + signaux GEO/E-E-A-T) | 100 moins des déductions pondérées selon la gravité, plafonnées par catégorie, sur les constats on-page, GEO et E-E-A-T. | Non intégré — définissez votre propre seuil. |
Arrêter le pipeline
La mécanique CI est la même quelle que soit la plateforme : une étape qui se termine avec un code non nul fait échouer le job. Pour réellement empêcher un PR ou une merge request d'être fusionnée, ajoutez ce job comme vérification de statut obligatoire dans la protection de branche ou les paramètres d'approbation des merge requests de votre hébergeur de dépôt — une étape échouée affiche seulement du rouge, elle n'empêche pas la fusion à elle seule.
Exemples de pipelines
Les cinq mêmes vérifications — Audit, Security Scan, Browser Audit, Repo Scan, Optimiseur SEO — écrites pour quatre plateformes. Choisissez votre système CI ci-dessous.
name: Scanverra checks
on: [pull_request]
jobs:
scanverra:
runs-on: ubuntu-latest
env:
API_KEY: ${{ secrets.SCANVERRA_API_KEY }}
TARGET_URL: https://staging.example.com # your deployed preview URL
steps:
- name: Website Audit (perf/SEO/a11y)
run: |
RES=$(curl -sf -X POST https://www.scanverra.com/api/audit \
-H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
-d "{\"url\":\"$TARGET_URL\"}")
echo "$RES" | jq .
PERF=$(echo "$RES" | jq '.performanceScore')
A11Y=$(echo "$RES" | jq '.accessibilityScore')
if (( $(echo "$PERF < 50" | bc -l) )) || (( $(echo "$A11Y < 90" | bc -l) )); then
echo "::error::Audit failed thresholds (perf=$PERF, a11y=$A11Y)"
exit 1
fi
- name: Security Scan
run: |
SCAN_ID=$(curl -sf -X POST https://www.scanverra.com/api/security-scan \
-H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
-d "{\"url\":\"$TARGET_URL\"}" | jq -r .scanId)
for i in $(seq 1 30); do
RESULT=$(curl -sf "https://www.scanverra.com/api/security-scan?id=$SCAN_ID" -H "X-API-Key: $API_KEY")
STATUS=$(echo "$RESULT" | jq -r .status)
[ "$STATUS" != "running" ] && break
sleep 10
done
RISK=$(echo "$RESULT" | jq '.riskScore')
echo "riskScore=$RISK"
[ "$STATUS" = "failed" ] && { echo "::error::Security scan failed to complete"; exit 1; }
[ "$RISK" -lt 70 ] && { echo "::error::Security risk score too low ($RISK)"; exit 1; }
true
- name: Browser Audit (JS errors, broken links)
run: |
TEST_ID=$(curl -sf -X POST https://www.scanverra.com/api/browser-test \
-H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
-d "{\"url\":\"$TARGET_URL\"}" | jq -r .testId)
for i in $(seq 1 30); do
RESULT=$(curl -sf "https://www.scanverra.com/api/browser-test?id=$TEST_ID" -H "X-API-Key: $API_KEY")
STATUS=$(echo "$RESULT" | jq -r .status)
[ "$STATUS" != "running" ] && break
sleep 10
done
ERRORS=$(echo "$RESULT" | jq '[.jsErrors[]? | select(.severity=="error")] | length')
[ "$STATUS" = "failed" ] && { echo "::error::Browser audit failed to complete"; exit 1; }
[ "$ERRORS" -gt 0 ] && { echo "::error::$ERRORS JS errors found"; exit 1; }
true
- name: Repo Scan (SAST, secrets, deps, quality gate)
run: |
SCAN_ID=$(curl -sf -X POST https://www.scanverra.com/api/repo/scan \
-H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
-d '{"owner":"your-org","repo":"your-repo","branch":"${{ github.head_ref }}"}' | jq -r .scanId)
for i in $(seq 1 30); do
RESULT=$(curl -sf "https://www.scanverra.com/api/repo/result?id=$SCAN_ID" -H "X-API-Key: $API_KEY")
STATUS=$(echo "$RESULT" | jq -r .status)
[ "$STATUS" != "running" ] && break
sleep 10
done
echo "$RESULT" | jq '{score, qualityGate}'
GATE=$(echo "$RESULT" | jq -r .qualityGate)
[ "$GATE" = "failed" ] && { echo "::error::Repo quality gate failed"; exit 1; }
true
- name: SEO Optimizer (on-page, GEO, E-E-A-T)
run: |
SCAN_ID=$(curl -sf -X POST https://www.scanverra.com/api/seo-optimizer \
-H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
-d "{\"url\":\"$TARGET_URL\"}" | jq -r .scanId)
for i in $(seq 1 30); do
RESULT=$(curl -sf "https://www.scanverra.com/api/seo-optimizer?id=$SCAN_ID" -H "X-API-Key: $API_KEY")
STATUS=$(echo "$RESULT" | jq -r .status)
[ "$STATUS" != "running" ] && break
sleep 10
done
SCORE=$(echo "$RESULT" | jq '.score')
echo "seoScore=$SCORE"
[ "$STATUS" = "failed" ] && { echo "::error::SEO scan failed to complete"; exit 1; }
[ "$SCORE" -lt 70 ] && { echo "::error::SEO score too low ($SCORE)"; exit 1; }
trueOù stocker la clé API en tant que secret, selon la plateforme :
- GitHub Actions : secret du dépôt — Settings → Secrets and variables → Actions.
- GitLab CI : variable CI/CD masquée — Settings → CI/CD → Variables.
- Azure Pipelines : variable de pipeline secrète (icône de cadenas) ou un Variable Group sous Pipelines → Library.
- Bitbucket Pipelines : Paramètres du dépôt > Pipelines > Variables du dépôt, marquées comme sécurisées.
- Jenkins : credential de type texte secret — Manage Jenkins → Credentials — référencé via
credentials(). L'image de l'agent a également besoin decurl,jqetbcinstallés ; les runnersubuntu-latesthébergés par GitHub et Azure les incluent déjà, l'imagealpinede GitLab non (d'où l'étapeapk add).
L'étape de l'analyseur de dépôts nécessite au préalable la connexion de votre compte GitHub/Bitbucket/Azure dans les paramètres Scanverra — l'analyse s'exécute avec le jeton qui y est stocké, et non avec un jeton transmis à chaque requête.
Sortie SARIF pour le code scanning
SARIF (Static Analysis Results Interchange Format) est le format JSON standard d'échange des résultats d'analyse de code. La CLI Scanverra produit du SARIF 2.1.0 avec --sarif, pour que les résultats apparaissent partout où SARIF est lu.
- GitHub code scanning : activez sarif: true dans scanverra/scan-action et les résultats apparaissent dans l'onglet Security et en annotations de pull request.
- SonarQube et les autres plateformes qui importent SARIF peuvent lire le même fichier.
- Bitbucket et GitLab : conservez le fichier SARIF comme artefact du pipeline, ou utilisez --json pour votre propre étape de reporting.
- uses: actions/checkout@v4
- uses: scanverra/scan-action@v1
with:
api-key: ${{ secrets.SCANVERRA_API_KEY }}
sarif: true # uploads the report to GitHub code scanning
# The job needs: permissions: { security-events: write }Seul l'analyseur de dépôts renvoie un champ qualityGate prêt à l'emploi. Pour les quatre autres outils, définissez vos propres seuils à l'avance et gardez-les cohérents entre les environnements (par exemple, ne comparez pas le score de sécurité d'une URL de staging à un seuil calibré pour la production).