Integrate with your pipeline
Gate releases by severity threshold, export SARIF/JSON/HTML artifacts, and keep security checks consistent in CI.
Current execution scope
- Runtime
- Prebuilt Go binaries do not require a Go compiler for basic scanning. Building from source requires Go 1.25+. Optional analyzers have their own runtime requirements; inspect coverage to confirm they actually ran.
- DAST
- URL and API probing inspect observable runtime behavior. Intrusive probes require --enable-active. An HTTP 200 response alone does not prove access to sensitive data.
- SAST
- Native static rules cover supported Go, JavaScript, Java and infrastructure patterns. Semgrep and the opt-in Python engine extend applicable checks. Language support does not imply complete framework or path coverage.
- SCA
- Go analysis uses govulncheck. Python and npm dependency scanners use supported manifests and lockfiles, including poetry.lock, Pipfile.lock and package-lock.json. Advisory queries can transmit package names and versions.
- Evidence and policy
- Correlation connects supported matching observations; not every finding has multiple sources. Strong single-source evidence can be sufficient. Severity, confidence, finding disposition and release recommendation are distinct.
- Coverage and human review
- An analyzer failure or missing required evidence can leave the release incomplete. A PASS is a result under the declared policy and coverage, not a guarantee of security or a human approval.
Benchmark tables below are historical, scoped observations. Counts and synthetic regression scores are not production precision/recall. No replacement measurements are claimed here.
Read deployment-specific data handlingSARIF 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. v3.0 changes finding identity again: re-save any baseline, rewrite any .fendix-ignore rule pinning a fingerprint: value, and expect GitHub alerts to re-file once under their new rule names. Rules matching by path, category or rule id are unaffected.
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).
GitHub Actions
- 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.sarifGitLab CI
# Generate a native GitLab CI template (saves as .gitlab-ci.yml)
fendix init --ci gitlab
# The generated job (or paste manually):
fendix_scan:
image: fendixapp/fendix:3.4.1
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]CircleCI
# 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: fendixapp/fendix:3.4.1
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.sarifJenkins
sh '''
fendix scan \
--url $API_URL \
--code ./src \
--format html \
--fail-on HIGH \
--save-baseline ./baseline.json \
--output fendix.html
'''Docker Compose
services:
fendix:
image: fendixapp/fendix:3.4.1
command: >
scan --url http://api:8080
--code /workspace/src
--format json --fail-on HIGH
--save-baseline /workspace/baseline.json
--output /workspace/results.json
volumes:
- .:/workspaceJira sync (v0.13.1+)
# 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=SECSlack + Teams alerts (v0.13.1+)
# 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 HIGHSaaS API & the dashboard
Beyond 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.
SaaS API — launch a scan
# 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"SaaS API — dashboard overview
# 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)
# 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