Skip to main content
Preloop stores SARIF 2.1.0 analyses so a GitHub-code-scanning upload step completes on an engine-hosted run and so a run can publish findings without GitHub at all. Every upload is normalized into the pieces a UI or check annotation needs — tool, rule, level, message, path, line range, fingerprint and suppression state — and the raw document is kept next to the normalized rows. Two surfaces share one store:

Authentication

The GitHub shim authenticates the job runtime token (the credential a workflow step receives as GITHUB_TOKEN): Bearer <token>, Octokit’s token <token>, or Basic x-access-token:<token>. The token is resolved through the job → run → submission chain, so the repository and run come from the credential, never from the request body. A token for another repository is rejected with 403. The native run API accepts the same job runtime token — it must belong to the {run_id} in the path — or the system token. Native errors keep the usual { "error": ... } envelope; the GitHub shim answers with GitHub’s { "message": ..., "documentation_url": ... } shape.

Upload limits

A payload over a limit is refused with 413; malformed base64, gzip, JSON or a SARIF version other than 2.1.0 is refused with 400.

GitHub payload

The shim accepts the payload upload-sarif sends: sarif is base64(gzip(SARIF)), commit_sha (the legacy endpoint’s name) or commit_oid (the current endpoint’s name), ref, and optionally tool_name, tool_names, checkout_uri, started_at, analysis_key, environment, base_ref and base_sha. A successful upload answers 202:
The status read the action polls answers {"processing_status":"complete","analyses_url":…,"sarif_id":…,"errors":[]}. Parsing is synchronous, so an analysis is complete by the time the first status read lands. Unknown ids and ids belonging to another repository both answer 404.

Native payload

POST /api/v1/runs/{run_id}/sarif accepts either the SARIF document itself:
or a wrapper whose sarif field is the document object or a base64(gzip(SARIF)) string. Optional wrapper fields: commit_sha, ref, tool_name, tool_names, checkout_uri, started_at, analysis_key, environment. Missing commit_sha/ref default to the run’s own submission. A Content-Encoding: gzip body is decompressed before parsing. The response is 202:
GET /api/v1/sarif/{analysis_id} returns the analysis metadata and the findings:

Idempotency

An upload is deduplicated on a content fingerprint over the repository, commit, ref, tool name and the sorted finding fingerprints — partialFingerprints first, then fingerprints, then a value derived from the location and message. Re-uploading the same findings for the same commit reuses the stored analysis (the same id/analysis_id), even when the raw bytes differ in formatting or result order. Findings inside one analysis are deduplicated on their fingerprint, so a producer that lists the same issue twice stores it once.

Storage

Raw SARIF is gzip-compressed in <state-dir>/sarif/<analysis-id>.sarif.gz. Metadata lives in the control database (sarif_analyses) and normalized findings in sarif_findings, so list/read work on every node sharing the database; the raw-document read is served by the node whose state directory holds the file.

Analysis-key derivation

github/codeql-action derives its analysis_key from GET /repos/{owner}/{repo}/actions/runs/{run_id} when the workflow does not supply one. The engine answers that read from the caller’s job token (the segment the action sends is NaN, because Octokit coerces the engine’s UUID run id) and returns the run’s repository-relative workflow path with workflow_url: null, which is the action’s documented fallback — so it does not make a second workflow request. Only the caller’s own run is served.