Gate releases by severity threshold, export SARIF/JSON/HTML artifacts, and keep security checks consistent in CI.
SARIF rule identifiers (engine v0.2+) look like fendix.<category>.<title-slug>. Pipeline baselines or suppressions keyed to older per-instance rule IDs must be regenerated against the new SARIF format.
v0.5+: drop-in GitHub Actions workflow · v0.14+: native GitLab CI & CircleCI templates · v0.16+: pre-commit hook (fendix hook install) & diff-aware scans on every commit
A complete reference GitHub Actions workflow lives at examples/github-actions/fendix-scan.yml — scan + cached baseline + SARIF upload + PR summary comment + a --fail-on HIGH gate. Drop it into .github/workflows/ and it works on every PR. For GitLab CI and CircleCI, run fendix init --ci gitlab or fendix init --ci circleci to generate a native config file.
New in v0.7: GitHub App (zero-config PR scans)
Skip the workflow YAML entirely. Install the Fendix GitHub App on a repo and every pull_request opens, synchronises, or reopens triggers a hybrid scan automatically — clone of the head SHA only (no history), fendix scan, then a Markdown PR comment plus a SARIF upload to the Code Scanning tab. Setup: docs/github-app.md covers App registration via app/manifest.yml and self-hosting fendix-app via Dockerfile.app (stateless ~250 MiB image, runs on Fly.io / Cloud Run / Render / Railway / ECS / k8s unchanged).
- name: Run Fendix scan
run: |
fendix scan \
--url ${{ secrets.API_URL }} \
--format sarif \
--fail-on HIGH \
--baseline ./baseline.json \
--save-baseline ./baseline.json \
--output fendix.sarif
- name: Upload SARIF
if: always()
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: fendix.sarif# v0.14+: generate a native GitLab CI template (saves as .gitlab-ci.yml)
fendix init --ci gitlab
# The generated job (or paste manually):
fendix_scan:
image: ghcr.io/abdel-rahmansaied/fendix:latest
script:
- fendix scan
--url "$API_URL"
--format json
--fail-on HIGH
--baseline ./baseline.json
--save-baseline ./baseline.json
--output fendix.json
artifacts:
paths: [fendix.json, baseline.json]# v0.14+: generate a native CircleCI config (saves as .circleci/config.yml)
fendix init --ci circleci
# The generated orb step (or paste manually):
version: 2.1
jobs:
security-scan:
docker:
- image: ghcr.io/abdel-rahmansaied/fendix:latest
steps:
- checkout
- run:
name: Fendix scan
command: |
fendix scan \
--url "$API_URL" \
--code ./src \
--format sarif \
--fail-on HIGH \
--output fendix.sarif
- store_artifacts:
path: fendix.sarifsh '''
fendix scan \
--url $API_URL \
--code ./src \
--format html \
--fail-on HIGH \
--save-baseline ./baseline.json \
--output fendix.html
'''services:
fendix:
image: ghcr.io/abdel-rahmansaied/fendix:latest
command: >
scan --url http://api:8080
--code /workspace/src
--format json --fail-on HIGH
--save-baseline /workspace/baseline.json
--output /workspace/results.json
volumes:
- .:/workspace# Idempotent: creates issues on first run, updates on re-run, skips already-filed
fendix jira \
--findings fendix.json \
--project SEC \
--server https://your-org.atlassian.net
# Environment variables (recommended over flags):
# FENDIX_JIRA_TOKEN=<personal-access-token>
# FENDIX_JIRA_USER=you@example.com
# FENDIX_JIRA_SERVER=https://your-org.atlassian.net
# FENDIX_JIRA_PROJECT=SEC# Slack webhook
fendix notify \
--findings fendix.json \
--slack-webhook "$SLACK_WEBHOOK_URL"
# Microsoft Teams webhook
fendix notify \
--findings fendix.json \
--teams-webhook "$TEAMS_WEBHOOK_URL"
# Both at once, only notify on HIGH+
fendix notify \
--findings fendix.json \
--slack-webhook "$SLACK_WEBHOOK_URL" \
--teams-webhook "$TEAMS_WEBHOOK_URL" \
--min-severity HIGHBeyond CI snippets, the Fendix SaaS API lets you launch scans programmatically, read an aggregated dashboard, and — with a self-hosted runner — push scans of private or internal targets straight into your workspace. Authenticate with an X-API-Key (fx_ prefix, Pro plan and up).
Self-hosted runners reach what the cloud can't.
A runner is an agent you host inside your network. It claims queued jobs, runs the engine against private targets, and submits the JSON report back to the dashboard — so internal APIs and white-box code scans land alongside everything else. Requires the Enterprise plan's self_hosted_runners feature.
# Launch a scan via the API (X-API-Key, fx_ prefix, shown once at creation)
curl -X POST https://api.fendix.dev/api/scans \
-H "X-API-Key: $FENDIX_API_KEY" \
-H 'Content-Type: application/json' \
-d '{ "mode": "blackbox", "url": "https://api.example.com", "fail_on": "high" }'
# Poll one scan (returns a {scan, findings} envelope)
curl https://api.fendix.dev/api/scans/$SCAN_ID -H "X-API-Key: $FENDIX_API_KEY"# Workspace overview: totals, severity + category breakdowns, 8-week trend.
# Scope to one org with ?organization=<uuid>, or omit for personal scans.
curl "https://api.fendix.dev/api/dashboard?organization=$ORG_ID" \
-H "X-API-Key: $FENDIX_API_KEY"
# → { total_scans, total_findings, by_severity, by_category, recent_scans, trend }# Self-hosted runner (Enterprise): scan private/internal targets in-network
# and push results to the dashboard. Register once (org admin) → fxr_ token.
TOKEN="fxr_..."
# Claim the next queued job (atomic) → or 204 No Content
curl -X POST https://api.fendix.dev/api/runners/claim -H "X-Runner-Token: $TOKEN"
# Run the engine locally, then submit the JSON report
fendix scan --code /repo --format json --output report.json
curl -X POST "https://api.fendix.dev/api/runners/jobs/$SCAN_ID/result" \
-H "X-Runner-Token: $TOKEN" -H 'Content-Type: application/json' \
--data-binary @report.json