Browse guides

Getting started

CI/CD

Scan from GitHub Actions, GitLab CI, Bitbucket Pipelines, Azure Pipelines, Jenkins, CircleCI or any CI system with one curl step and a CI token, and fail the pipeline when the scan finds what your gate does not allow.

Guide type: Product guide

In this guide

Use the scan API from any system that can make an HTTPS request. It starts the same hosted scan as a console upload, and there is nothing to install on the build machine. A connected repository needs no step at all: connecting your code host scans every push to the default branch and every pull request for you.

Authorise

A Super Admin or admin who also has the SuperUser role creates a CI token in the console under Settings, Integrations, CI and pull requests, with a fresh code from their authenticator. The token is shown once: store it in your CI system's secrets as MAGDOX_CI_TOKEN, and the step sends it as a bearer token.

Add the scan step

bashexample
git archive --format=tar.gz -o source.tar.gz HEAD
curl --fail-with-body -X POST https://api.magdox.io/v1/scan-jobs/upload \
  -H "Authorization: Bearer $MAGDOX_CI_TOKEN" \
  -H "X-Magdox-Repository: payments-api" \
  -H "X-Magdox-Branch: $BRANCH" \
  -H "X-Magdox-Commit: $(git rev-parse HEAD)" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @source.tar.gz

The step packs the checked-out commit with git archive, so the .git folder and untracked files stay behind, and sends the archive as the request body. Set BRANCH from your CI system: CI_COMMIT_REF_NAME on GitLab, BITBUCKET_BRANCH on Bitbucket Pipelines or BRANCH_NAME on Jenkins, for example.

Add the scan step
HeaderMeaning
AuthorizationBearer, then the CI token
X-Magdox-RepositoryThe repository key: 3 to 64 lower-case letters, digits, dots, dashes or underscores, starting and ending with a letter or digit. A key that does not exist yet creates the repository with its first scan
X-Magdox-BranchThe branch the code comes from, up to 255 bytes. Percent-encode anything outside printable ASCII, and send a % in the name as %25
X-Magdox-CommitOptional: the commit, 7 to 64 lower-case hexadecimal characters
X-Magdox-Pull-RequestOptional, for a pull or merge request: its number. Send it with the next two headers, or none of them
X-Magdox-Providergithub, gitlab or bitbucket: where the pull request lives. MAGDOX posts a findings summary on it with the provider token a Super Admin or admin saved under Settings, Integrations, CI and pull requests, Optional pull request comments for CI uploads
X-Magdox-Provider-RepositoryThe repository path there, for example group/subgroup/project on GitLab

Archives can be .zip or .tar.gz, sent as the body or as the file field of a multipart form. One request to the hosted API carries an archive of about 4 MB at most, so send a larger one, up to 200 MB, as a staged upload (see the note below). The address takes no query string: a request that carries one is refused. The answer is 201 with the scan job, and its id lets you follow the scan.

Fail the pipeline on the result

The upload answers with the scan job as JSON: set JOB_ID to its id field. The step then asks for the verdict until the scan has finished, and exits with an error when your gate fails. Ready-made steps for GitHub Actions, GitLab CI, Bitbucket Pipelines, Azure Pipelines, Jenkins and CircleCI are in the console under Settings, Integrations, CI and pull requests.

bashexample
curl --fail-with-body -X POST https://api.magdox.io/v1/ci/verdict \
  -H "Authorization: Bearer $MAGDOX_CI_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"job_id\": \"$JOB_ID\", \"gate\": \"pr\", \"fail_on\": \"high\", \"base_branch\": \"$BASE_BRANCH\"}"
Fail the pipeline on the result
FieldMeaning
gatepr counts only findings the change introduced, those not open on the base branch; release counts every open finding
base_branchThe branch the change merges into. Required with the pr gate, and not sent with the release gate
fail_onThe lowest severity that fails the gate: critical, high, medium, low or info, or none to report without failing
state (answer)pending, passed, failed or error. Ask again, at most every few seconds, while it is pending
stage (answer)While pending, where the scan is in a few plain words: Queued, Unpacking the code, Scanning and the passes running (for example Scanning Code and Infrastructure as code) or Saving results. The ready-made steps print a line each time it changes
blocking and findings (answer)How many findings failed the gate, and up to 20 of them with rule, severity, file and line, never code

Archived findings, including those marked as false positives, never block a pipeline. Nor do accepted risks, or snoozed findings until their snooze ends.

The full CI API, every call a CI token can make with its fields and answers, is published as an OpenAPI 3.1 reference.

CI API reference (OpenAPI)

Answers to handle

Answers to handle
StatusMeaning
400The archive is not a valid .zip or .tar.gz or breaks a limit, a header is missing or not valid, or the address carries a query string; the message says which
401The token is not valid: revoked, mistyped or expired
402The organisation has no active plan
403The person who created the token no longer has the SuperUser role, or the organisation is suspended
413The archive is larger than 200 MB
429The organisation reached its daily limit of 200 hosted scans in any 24 hours, uploads are busy, or requests came too often; retry after the time in Retry-After

Send an archive larger than about 4 MB straight to storage. Send POST /v1/scan-jobs/upload/stage with the same Authorization and X-Magdox headers as the upload, Content-Type: application/json, and a JSON body holding size (the archive's size in bytes) and sha256 (its SHA-256 in hexadecimal). The answer holds the job, an upload address valid for five minutes and the headers to send with it. PUT the archive to that address with exactly those headers, then send POST /v1/scan-jobs/start with a JSON body holding id, the job's id, to start the scan.

Keep the token safe

  • Give MAGDOX_CI_TOKEN only to jobs your organisation trusts, never to jobs that build pull requests from forks.
  • The token belongs to the organisation: its label does not limit it to one repository. It expires 90 days after it is created, and the console shows the date. It stops working sooner when it is revoked, or when the person who created it changes or resets their password, changes their email, removes an authenticator, leaves the organisation or loses the SuperUser role. Create a new token and update the secret before then, and revoke a token in the console when its pipeline is retired.
  • The step sends only the archive. MAGDOX deletes it when the scan ends, or within 24 hours when an admin keeps uploads for proofs, and keeps the results.