CI/CD integration
Every pipeline does the same four things: install vulkro-sf, sign in with a CI key, scan, and fail the job on exit code 1.
curl -fsSL https://dist.vulkro.com/install-sf.sh | bash
vulkro-sf login # uses VULKRO_ACCOUNT_TOKEN
vulkro-sf scan ./force-app --format sarif > vulkro-sf.sarif
Create the CI key in the portal (app.vulkro.com) and store it as a secret named VULKRO_ACCOUNT_TOKEN. Without it the scan exits 1 with license_required. An air-gapped runner sets VULKRO_LICENSE_B64 to a base64 .lic file instead. See Licensing.
GitHub Actions
# .github/workflows/vulkro-sf.yml
name: Vulkro for Salesforce
on:
pull_request:
branches: [main]
push:
branches: [main]
permissions:
contents: read
security-events: write # to upload SARIF
jobs:
scan:
runs-on: ubuntu-latest
env:
VULKRO_ACCOUNT_TOKEN: ${{ secrets.VULKRO_ACCOUNT_TOKEN }}
steps:
- uses: actions/checkout@v4
- name: Install vulkro-sf
run: curl -fsSL https://dist.vulkro.com/install-sf.sh | bash
- name: Sign in with the CI key
run: vulkro-sf login
- name: Scan
run: vulkro-sf scan ./force-app --format sarif > vulkro-sf.sarif
- name: Upload SARIF
if: always()
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: vulkro-sf.sarif
category: vulkro-sf
if: always() uploads the SARIF even when the scan step fails on findings.
GitLab CI
# .gitlab-ci.yml
vulkro-sf-scan:
image: ubuntu:24.04
before_script:
- apt-get update && apt-get install -y curl ca-certificates
- curl -fsSL https://dist.vulkro.com/install-sf.sh | bash
- vulkro-sf login # VULKRO_ACCOUNT_TOKEN: a masked CI/CD variable
script:
- vulkro-sf scan ./force-app --format sarif > vulkro-sf.sarif
artifacts:
when: always
paths:
- vulkro-sf.sarif
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
Bitbucket Pipelines
# bitbucket-pipelines.yml
image: ubuntu:24.04
pipelines:
pull-requests:
'**':
- step:
name: Vulkro for Salesforce
script:
- apt-get update && apt-get install -y curl ca-certificates
- curl -fsSL https://dist.vulkro.com/install-sf.sh | bash
- vulkro-sf login # VULKRO_ACCOUNT_TOKEN: a secured repository variable
- vulkro-sf scan ./force-app --format sarif > vulkro-sf.sarif
artifacts:
- vulkro-sf.sarif
Fail only on new findings
To keep existing findings from blocking every pull request, compare against the target branch with --gate-vs. It scans the base ref too and exits 1 only for findings the change introduced. Check out the full history first (fetch-depth: 0 on GitHub).
vulkro-sf scan ./force-app --gate-vs origin/main
--min-confidence high makes the gate stricter while a team works through its backlog. See Baselines.
Exit codes
| Code | Meaning | In CI |
|---|---|---|
0 | No findings | Pass |
1 | Findings reported, or no licence (license_required on stderr) | Fail |
2 | Error (bad arguments, unreadable path, internal failure) | Fail, read stderr |
Air-gapped runners
Bake a pinned binary into the runner image (VULKRO_SF_VERSION), set VULKRO_OFFLINE=1, and license it with VULKRO_LICENSE_B64 or vulkro-sf activate. A scan then sends nothing anywhere. Live-org commands are refused under VULKRO_OFFLINE, because they need HTTPS to your Salesforce instance.
See also: GitHub Actions, GitLab CI, SARIF output