CI/CD Integration
Guias
Integração CI/CD
Todo endpoint de scanner aceita uma chave de API, então qualquer um deles pode rodar como uma etapa de pipeline. Esta página explica o que significa a saída de cada ferramenta, como transformá-la em um gate de aprovação/reprovação, e traz exemplos funcionais para quatro plataformas de CI.
Verificação de código com a GitHub Action
O jeito mais rápido de verificar código na CI. Ela roda a @scanverra/cli no runner (seu código nunca sai dele), faz o job falhar a partir da gravidade escolhida e envia SARIF ao 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 pontuação por ferramenta
O que cada endpoint retorna e em qual valor basear seu limite.
| Ferramenta | Saída | Base | Gate integrado |
|---|---|---|---|
| Auditoria de site | 4 pontuações de 0 a 100: performance, seo, accessibility, bestPractices | Pontuações por categoria de 0 a 100. Convenção: ≥90 bom, 50-89 precisa melhorar, <50 ruim. | Nenhum integrado — escolha seu próprio limite para cada pontuação. |
| Verificação de segurança | riskScore de 0 a 100 (quanto maior, mais seguro) | 100 menos deduções ponderadas por gravidade e limitadas por categoria. Faixas de nota: A+ ≥90 … F <30. | Nenhum integrado — escolha seu próprio limite. |
| Auditoria de navegador | Arrays: jsErrors, brokenLinks, formIssues, imgIssues (cada um com severity: "error" | "warning") | Sem pontuação agregada. | Baseie o gate na contagem de problemas, por exemplo qualquer severity === "error". |
| Scanner de repositórios | score de 0 a 100 + qualityGate: "passed" | "failed" | 100 menos deduções ponderadas por gravidade, limitadas por categoria. | Integrado — reprova automaticamente com qualquer descoberta crítica ou score < 60. |
| Otimizador de SEO | score de 0 a 100 (SEO on-page + sinais GEO/E-E-A-T) | 100 menos deduções ponderadas por gravidade, limitadas por categoria, sobre achados on-page, GEO e E-E-A-T. | Não integrado — defina seu próprio limite. |
Interrompendo o pipeline
A mecânica de CI é a mesma independentemente da plataforma: uma etapa que termina com código diferente de zero falha o job. Para realmente bloquear a mesclagem de um PR ou merge request, adicione esse job como uma verificação obrigatória/status check nas configurações de proteção de branch ou aprovação de merge request do seu host de repositório — uma etapa falha sozinha só aparece em vermelho, ela não impede uma mesclagem por si só.
Exemplos de pipeline
As mesmas cinco verificações — Auditoria, Verificação de segurança, Auditoria de navegador, Scanner de repositórios, Otimizador de SEO — escritas para quatro plataformas. Escolha seu sistema de CI abaixo.
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; }
trueOnde armazenar a chave de API como um segredo, por plataforma:
- GitHub Actions: secret do repositório — Settings → Secrets and variables → Actions.
- GitLab CI: variável de CI/CD mascarada — Settings → CI/CD → Variables.
- Azure Pipelines: variável secreta de pipeline (ícone de cadeado) ou um Variable Group em Pipelines → Library.
- Bitbucket Pipelines: Configurações do repositório > Pipelines > Variáveis do repositório, marcadas como seguras.
- Jenkins: credencial Secret text — Manage Jenkins → Credentials — referenciada via
credentials(). A imagem do agente também precisa tercurl,jqebcinstalados; os runnersubuntu-latesthospedados pelo GitHub e pela Azure já os incluem, mas a imagemalpinedo GitLab não (daí a etapaapk add).
A etapa do Scanner de repositórios precisa que sua conta do GitHub/Bitbucket/Azure esteja conectada previamente nas configurações da Scanverra — a varredura roda usando o token armazenado ali, não um passado por requisição.
Saída SARIF para code scanning
SARIF (Static Analysis Results Interchange Format) é o formato JSON padrão que as ferramentas de code scanning usam para trocar resultados. A CLI da Scanverra gera SARIF 2.1.0 com --sarif, então os resultados aparecem onde o SARIF for lido.
- GitHub code scanning: defina sarif: true no scanverra/scan-action e os resultados aparecem na aba Security e como anotações nos pull requests.
- SonarQube e outras plataformas que importam SARIF podem ler o mesmo arquivo.
- Bitbucket e GitLab: guarde o arquivo SARIF como artefato do pipeline ou use --json na sua própria etapa de relatório.
- 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 }Somente o Scanner de repositórios retorna um campo qualityGate pronto para uso. Para as outras quatro ferramentas, defina seus próprios limites antecipadamente e mantenha-os consistentes entre os ambientes (por exemplo, não compare a pontuação de segurança de uma URL de staging com um limite ajustado para produção).