Docs/API et Webhooks/Intégration SARIF

Intégration SARIF

Poussez la sortie SARIF de tout outil de scan de code (CodeQL, Semgrep, Snyk, Checkmarx, Trivy, Bandit, GitLab SAST, OWASP ZAP) vers SecureCodingHub. Chaque CWE unique dans l'exécution devient une affectation sur le compte de l'auteur du commit — généralement en quelques secondes après la fusion.

Pourquoi CI est le bon déclencheur

Les déploiements génériques « chaque développeur suit le même cours OWASP Top 10 » ont une faible complétion et une rétention plus faible. L'intégration CI inverse le modèle : la formation est attribuée spécifiquement au développeur qui a introduit un problème, spécifiquement sur la classe de vulnérabilité dont il a besoin, avec une échéance de 14 jours liée à la fusion qui l'a exposée. C'est ce que les auditeurs appellent la formation au codage sécurisé juste-à-temps.

Comment ça fonctionne de bout en bout

1

Votre outil SAST produit un fichier SARIF 2.1.0 dans le cadre d'un job CI.

2

Une étape CI POST le fichier à /api/public/v1/sarif avec les métadonnées du commit comme en-têtes de requête.

3

SecureCodingHub analyse chaque result, extrait ses tags CWE et cherche chacun dans notre carte interne CWE-vers-sujet.

4

Pour chaque sujet mappé unique, une affectation est créée sur l'utilisateur dont l'e-mail correspond à X-Commit-Author-Email, avec une échéance de 14 jours.

5

Un webhook sarif.ingested se déclenche avec la même charge utile que la réponse synchrone renvoie.

Le point de terminaison

POST /api/public/v1/sarif
Authorization: Bearer scs_live_…
Content-Type: application/json
X-Source: github-codeql
X-Repo: acme/payments-api
X-Branch: main
X-Commit-Sha: 9c2f4b8e0c1d…
X-Commit-Author-Email: jane.smith@acme.com

<SARIF 2.1.0 JSON body, up to 10 MB>

Portée requise : sarif:ingest.

En-têtes de requête

En-têteRequisSignification
X-SourceIdentifiant d'outil libre — par exemple github-codeql, snyk, semgrep-ci. Enregistré pour l'audit.
X-RepoSlug du dépôt (owner/name). Enregistré pour l'audit.
X-BranchNom de la branche. Enregistré pour l'audit.
X-Commit-SharecommandéHash du commit. Utilisé avec X-Repo pour l'idempotence de 24 heures.
X-Commit-Author-EmailrecommandéE-mail de l'auteur du commit. Résolu en utilisateur SecureCodingHub ; les affectations leur sont attachées. Si l'e-mail ne correspond pas à un utilisateur, l'exécution est toujours ingérée mais aucune affectation n'est créée.

Réponse

{
  "ingestionId": "7e4c9d09-86ca-4125-bbd8-7fae54c8f654",
  "findingsCount": 2,
  "uniqueCwes": ["CWE-79", "CWE-89"],
  "unmappedCwes": [],
  "assignmentsCreated": 12,
  "notes": []
}
  • findingsCount — nombre total d'entrées result analysées.
  • uniqueCwes — ensemble des CWE mentionnés à travers toutes les découvertes.
  • unmappedCwes — CWE que nous n'avons pas pu mapper à un sujet. Envoyez à support si vous voulez que la couverture soit ajoutée.
  • assignmentsCreated — nombre de nouveaux enregistrements d'affectation (les doublons existants sont ignorés).
  • notes — avertissements non fatals (par exemple format d'e-mail d'auteur de commit invalide).

Idempotence

Réingérer le même fichier SARIF est sûr. Le tuple (organization, repo, commit_sha) est la clé d'idempotence — un doublon dans une fenêtre de 24 heures renvoie 200 avec l'ingestionId d'origine et assignmentsCreated: 0. Cela signifie que les workflows CI qui se réexécutent sur le même commit (rebase, réexécution manuelle, push vers PR) n'empilent pas les affectations en double.

Exemple GitHub Actions

Insérez cette étape dans un workflow après que votre job CodeQL ou Semgrep a produit results.sarif :

- name: Send SARIF to SecureCodingHub
  if: always()
  run: |
    curl -sS -f -X POST \
      -H "Authorization: Bearer $SCH_API_KEY" \
      -H "Content-Type: application/json" \
      -H "X-Source: github-codeql" \
      -H "X-Repo: ${{ github.repository }}" \
      -H "X-Branch: ${{ github.ref_name }}" \
      -H "X-Commit-Sha: ${{ github.sha }}" \
      -H "X-Commit-Author-Email: ${{ github.event.head_commit.author.email }}" \
      --data-binary @results.sarif \
      https://api.limeplate.com/api/public/v1/sarif
  env:
    SCH_API_KEY: ${{ secrets.SCH_API_KEY }}

Stockez la clé dans les secrets du dépôt ou de l'organisation. La garde if: always() garantit que les découvertes sont envoyées même si le job de sécurité a marqué le build comme échoué — ce qui est précisément quand vous voulez le plus que les affectations de formation se déclenchent.

Exemple GitLab CI

send_sarif_to_sch:
  stage: post-security
  image: curlimages/curl:latest
  needs: ["sast"]
  script:
    - >
      curl -sS -f -X POST
      -H "Authorization: Bearer $SCH_API_KEY"
      -H "Content-Type: application/json"
      -H "X-Source: gitlab-sast"
      -H "X-Repo: $CI_PROJECT_PATH"
      -H "X-Branch: $CI_COMMIT_BRANCH"
      -H "X-Commit-Sha: $CI_COMMIT_SHA"
      -H "X-Commit-Author-Email: $GITLAB_USER_EMAIL"
      --data-binary @gl-sast-report.json
      https://api.limeplate.com/api/public/v1/sarif
  rules:
    - if: $CI_COMMIT_BRANCH

Le rapport GitLab SAST est compatible SARIF — aucune conversion nécessaire.

Exemple Semgrep

semgrep ci --sarif --output results.sarif
curl -sS -f -X POST \
  -H "Authorization: Bearer $SCH_API_KEY" \
  -H "X-Source: semgrep" \
  -H "X-Repo: $REPO" \
  -H "X-Commit-Sha: $COMMIT" \
  -H "X-Commit-Author-Email: $AUTHOR_EMAIL" \
  --data-binary @results.sarif \
  https://api.limeplate.com/api/public/v1/sarif

Couverture CWE

SecureCodingHub mappe actuellement OWASP Top 10, OWASP API Security Top 10, OWASP Mobile Top 10 et CWE Top 25 aux sujets de formation. Les découvertes étiquetées avec des IDs CWE en dehors de cet ensemble apparaissent dans la liste unmappedCwes de la réponse. Formats courants que nous acceptons du côté tag SARIF :

  • external/cwe/cwe-89 (standard CodeQL)
  • cwe-89
  • CWE-89
  • cwe:89

Les tags sont lus à la fois depuis result.properties.tags et le tool.driver.rules[].properties.tags correspondant.

Plafond de taille de corps

Le point de terminaison SARIF accepte les charges utiles jusqu'à 10 Mo non compressées. La plupart des rapports SAST réels pour un seul dépôt restent bien en dessous de cela. Si vous avez un rapport de monorepo à l'échelle entreprise qui dépasse le plafond, divisez par pilote d'outil (une exécution SARIF par pilote) et POSTez chaque partie — elles seront idempotentes sous le même SHA de commit.

Modes d'échec

SymptômeCause probable
200 avec assignmentsCreated: 0L'e-mail de l'auteur du commit n'est pas un utilisateur dans votre organisation, ou tous les CWE étaient déjà attribués.
200 avec unmappedCwes non videLes découvertes référencent des CWE qui n'ont pas encore de sujet de formation. Transmettez la liste au support.
400 empty_bodyCurl manquait --data-binary @file.sarif.
413 body_too_largeSARIF dépasse 10 Mo. Divisez par pilote.
200 avec une entrée notes commençant par idempotency:Le même (repo, commit) a déjà été ingéré dans les 24 h. La réponse est par ailleurs identique à une nouvelle ingestion avec assignmentsCreated: 0 — sûre à ignorer.