Enforce one release decision in every pipeline
Fendix runs inside your CI/CD, collects or imports the security evidence, checks the coverage you require, applies the release policy committed in your repository, and returns a result the pipeline can enforce — the same way in every repository.
What happens in the pipeline
Seven steps run on every pull request or merge. The scanner steps produce evidence; the policy steps turn it into a result.
- 1
CI/CD job
A job on pull request or merge runs the Fendix engine from the signed container image or binary.
- 2
Collect or import evidence
Fendix's own DAST, SAST, SCA and secrets analysis, plus SARIF from tools you already run through fendix import.
- 3
Validate required coverage
Every analyzer reports whether it ran. --fail-on-coverage-gap or --require-analyzers fail the job when required evidence is missing.
- 4
Evaluate findings and risk disposition
Confidence gating, test-code handling, baselines and .fendix-ignore suppressions with expiry dates.
- 5
Apply the release policy
The threshold and confidence enforcement committed in .fendix.yaml, reviewed like any other change.
- 6
Return an enforceable result
The CLI marks each finding BLOCK, WARN or INFO and sets the exit code. The hosted workspace turns the same evidence into one decision: PASS, WARN, BLOCK or INCOMPLETE.
- 7
Publish the record
SARIF 2.1.0 for code scanning, JSON for automation, HTML for people. Hosted decisions are stored with their rationale and policy version.
What the exit code means
- 0
- Nothing reached BLOCK under your policy.
- 1
- At least one finding reached BLOCK. The report says which, and why.
- 2
- The run could not produce a trustworthy result: an error, or a required coverage gap when you enabled the coverage flags.
By default, severity alone does not fail the job: a finding at or above --fail-on blocks only when its deterministic confidence and evidence support the claim, and every finding held at WARN says why. --enforce-confidence=false restores the legacy severity-only gate, and a finding that blocks only because of it is marked as a policy override.
How policy and confidence decide a blockGitHub Actions: Fendix Security Scan
The recommended GitHub integration: a public GitHub Marketplace Action that installs a signature-verified Fendix release and gates the job on your release policy.
Pin the Action to the full commit of its v3.7.0 release, with an explicit version. The commit fixes the Action's own code, which cannot change under you; version selects the engine release it installs, here v3.7.0. Never reference @main; it changes without review.
permissions:
contents: read
security-events: write # SARIF upload
actions: read # SARIF upload in a private or internal repository
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0 # diff mode compares against the PR base ref
- uses: Fendix-app/Fendix@90adf6a5082be0661b4156566a3a99609ed4792d # v3.7.0
with:
version: v3.7.0
code: .
fail-on: HIGHRequired analyzers
For a code scan the Action requires Semgrep. It runs Semgrep on a bundled rule before the scan and passes the requirement to the engine, so a missing, broken or hung Semgrep fails the job with exit 2 instead of letting it pass without Semgrep. Semgrep needs glibc 2.34 or later, or musl 1.2 or later, on x86_64 or aarch64; GitHub-hosted runners and the official image qualify. On an older self-hosted runner, run Fendix from the official image in a container job, or set require-analyzers: none to scan without Semgrep, and the gate then warns that coverage is not enforced.
The @v1 tag
Since 2026-10-27, @v1 resolves to the v3.7.0 release commit, the same Action code as the pin above, and literal @v1 with version: v3.7.0 passed the same end-to-end scenarios. The tag moves when a later compatible release is published, so pin the commit when the reference must never change.
Private repositories
SARIF upload to a private or internal repository requires GitHub Code Security. Without it, set upload-sarif: 'false': the scan and the release gate still run.
Pipeline recipes
Copy a recipe, then commit the policy in .fendix.yaml so every pipeline reads the same gate. Code is not localized.
GitLab CI
# Generate a native GitLab CI include file (.gitlab-ci.fendix.yml)
fendix init --ci gitlab
# The generated job (or paste manually):
fendix_scan:
image: fendixapp/fendix:3.7.0
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 snippet (.circleci/fendix-config.yml)
fendix init --ci circleci
# The generated orb step (or paste manually):
version: 2.1
jobs:
security-scan:
docker:
- image: fendixapp/fendix:3.7.0
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.7.0
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 issue sync
# Required Jira configuration
export FENDIX_JIRA_URL=https://your-org.atlassian.net
export FENDIX_JIRA_PROJECT_KEY=SEC
export FENDIX_JIRA_EMAIL=you@example.com
export FENDIX_JIRA_API_TOKEN=<personal-access-token>
# Optional severity floor (default: HIGH)
export FENDIX_JIRA_MIN_SEVERITY=HIGH
# Idempotent: creates once, then skips an existing fendix-id label
fendix jira --findings fendix.jsonSlack and Microsoft Teams alerts
# Configure either or both incoming webhooks
export FENDIX_SLACK_WEBHOOK_URL="$SLACK_WEBHOOK_URL"
export FENDIX_TEAMS_WEBHOOK_URL="$TEAMS_WEBHOOK_URL"
# Optional severity floor (default: CRITICAL)
export FENDIX_NOTIFY_MIN_SEVERITY=HIGH
fendix notify --findings fendix.jsonGenerated workflows and the GitHub App
fendix init --ci github, --ci gitlab or --ci circleci writes a native workflow that installs a pinned Fendix release and verifies its signature before running. The GitLab workflow keeps the SARIF file as an ordinary artifact, since Fendix SARIF is not GitLab's SAST report format.
Prefer to own the workflow file? The tested reference workflow is an alternative for teams that want direct workflow ownership, an immutable container digest and explicitly pinned third-party Actions. It runs the official v3.7.0 container and includes a cached baseline, SARIF upload, a PR summary comment and a --fail-on HIGH gate: examples/github-actions/fendix-scan.yml
Prefer no workflow file? A self-hosted GitHub App scans each pull request and posts a summary comment plus SARIF annotations. Setup and deployment are documented in docs/github-app.md
Technical reference: Require coverage before a release · What each exit code means · SARIF export for code scanning · Import SARIF from other scanners
Hosted workspace and private targets
The hosted workspace stores scan history, findings, and release decisions. Its backend HTTP routes are a private service interface, so supported automation uses the CLI, CI/CD integrations, JSON reports, and SARIF documented above.
Self-hosted runners reach what the cloud can't.
An Enterprise runner performs target and source analysis inside your network and returns the resulting report to your hosted workspace. Fendix provides the supported runner package and connection values during onboarding; the wire protocol is not a public extension contract.
Customer-facing runner setup
- Create or select the Enterprise workspace and request runner enablement through your Fendix contact.
- Register the runner from workspace settings and place the one-time connection values in your approved secret store.
- Deploy the supported runner package inside the network that can reach the authorized targets, then verify health from the workspace.
- Use the documented troubleshooting and support path for connection failures; do not implement a custom client against backend service routes.
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 Python engine attempted by code scans extend applicable checks when available. 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.
Upgrading from an older engine
v3.0 changed how findings are identified, and v3.6.0 applies baselines, .fendix-ignore rules and SARIF identity to each occurrence. After upgrading from before v3.0, re-save baselines once and rewrite any .fendix-ignore rule that pins a fingerprint: value; expect GitHub code-scanning alerts to re-file once under their new rule IDs.