Skip to main content

GitLab CI

Run vulkro-sf on every merge request and on the default branch.

# .gitlab-ci.yml
stages:
- security

vulkro-sf-scan:
stage: security
image: ubuntu:24.04
before_script:
- apt-get update && apt-get install -y curl ca-certificates git
- curl -fsSL https://dist.vulkro.com/install-sf.sh | bash
- vulkro-sf login # VULKRO_ACCOUNT_TOKEN: a masked CI/CD variable holding your CI key
script:
- vulkro-sf scan ./force-app --format sarif > sf.sarif
artifacts:
when: always
paths:
- sf.sarif
expire_in: 30 days
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

Create the CI key in the portal (app.vulkro.com) and store it as a masked variable named VULKRO_ACCOUNT_TOKEN. Without it the job fails with exit 1 and license_required. when: always keeps the SARIF even when the scan fails on findings.

Fail only on new findings​

In merge request pipelines, compare with the merge request's base commit:

script:
- |
if [ -n "$CI_MERGE_REQUEST_DIFF_BASE_SHA" ]; then
vulkro-sf scan ./force-app --gate-vs "$CI_MERGE_REQUEST_DIFF_BASE_SHA" --format sarif > sf.sarif
else
vulkro-sf scan ./force-app --format sarif > sf.sarif
fi

The SARIF holds every finding; the job fails only on findings the merge request introduced. Set GIT_DEPTH: 0 so the base commit is available. To post a summary on the merge request, use --format gitlab-mr on a full scan. See Baselines.

Exit codes​

0 passes; 1 fails the job (findings, or no licence); 2 fails it as an error (read the job log). Use allow_failure: true for a warm-up period while the team works through existing findings.

Air-gapped runners​

Bake a pinned binary into the runner image (VULKRO_SF_VERSION), set VULKRO_OFFLINE=1, and license it by setting VULKRO_LICENSE_B64 to a base64 .lic file in a masked variable, or with vulkro-sf activate <file.lic> at image build. See Licensing.

Live-org audits on a schedule​

Use the Salesforce CLI's JWT bearer flow with a dedicated, certificate-based Connected App (scopes api and refresh_token, never Full) and a read-only CI user with API Enabled and View Setup and Configuration.

vulkro-sf-org-report:
stage: security
image: ubuntu:24.04
rules:
- if: $CI_PIPELINE_SOURCE == "schedule"
before_script:
- apt-get update && apt-get install -y curl ca-certificates nodejs npm
- curl -fsSL https://dist.vulkro.com/install-sf.sh | bash
- npm install -g @salesforce/cli
- vulkro-sf login
- echo "$SF_JWT_SERVER_KEY" > server.key
- sf org login jwt --client-id "$SF_CONSUMER_KEY" --jwt-key-file server.key --username "$SF_USERNAME" --alias prod-ci
- rm server.key
script:
- vulkro-sf org report --target-org prod-ci -O posture.html
artifacts:
when: always
paths:
- posture.html

Store SF_JWT_SERVER_KEY as a protected, masked variable.

See also: CI/CD integration, GitHub Actions, The sf CLI handoff