Skip to main content
TutorialsJan 13, 20269 min read

Setting up CI/CD Security with GuardianX

Shift security left: wire GuardianX into GitHub Actions, GitLab CI, and Jenkins. Scan every PR, block merges on Critical findings, and post inline code comments.

AR
Arjun Reddy
DevSecOps Engineer

Shifting security left — running scans on every pull request instead of waiting for a pre-release VAPT — reduces remediation cost by 10-100x. This tutorial shows how to wire GuardianX into your CI/CD pipeline so every PR is automatically scanned, with findings posted as inline code comments and Critical issues blocking merges.

Why CI/CD Security?

A vulnerability caught in development costs minutes to fix. The same vulnerability caught in production costs hours (incident response, hotfix deploy, customer comms). CI/CD scanning catches issues at the cheapest possible point in the lifecycle.

Prerequisites

  • A GuardianX account (any tier)
  • A repository on GitHub, GitLab, or Bitbucket
  • CI/CD runner: GitHub Actions, GitLab CI, or Jenkins
  • GuardianX API token (Settings → API Tokens → Generate)

Option 1: GitHub Actions

Add this workflow to .github/workflows/guardianx.yml:

yaml
name: GuardianX Security Scan on: pull_request: branches: [main, develop] jobs: scan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Install GuardianX CLI run: | curl -fsSL https://guardianx.cloud/install.sh | sh - name: Run SAST scan env: GUARDIANX_TOKEN: ${{ secrets.GUARDIANX_TOKEN }} run: | guardianx scan sast \ --path . \ --format json \ --output findings.json \ --fail-on critical - name: Post inline comments if: always() env: GUARDIANX_TOKEN: ${{ secrets.GUARDIANX_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | guardianx comment github \ --findings findings.json \ --pr ${{ github.event.pull_request.number }}

Add GUARDIANX_TOKEN as a repository secret (Settings → Secrets → Actions).

Option 2: GitLab CI

Add to .gitlab-ci.yml:

yaml
guardianx-sast: image: node:20 stage: test only: - merge_requests script: - curl -fsSL https://guardianx.cloud/install.sh | sh - guardianx scan sast --path . --format json --output findings.json --fail-on critical - guardianx comment gitlab --findings findings.json --mr $CI_MERGE_REQUEST_IID artifacts: reports: sast: findings.json

Set GUARDIANX_TOKEN as a CI/CD variable (Settings → CI/CD → Variables).

Option 3: Jenkins

Add a Jenkinsfile stage:

groovy
pipeline { agent any stages { stage('GuardianX SAST') { steps { sh 'curl -fsSL https://guardianx.cloud/install.sh | sh' withCredentials([string(credentialsId: 'guardianx-token', variable: 'TOKEN')]) { sh ''' guardianx scan sast \ --path . \ --format json \ --output findings.json \ --fail-on critical \ --token $TOKEN ''' } } } } }

Configuration: fail-on Thresholds

GuardianX CLI supports several thresholds:

  • --fail-on critical — exit code 1 if any Critical finding
  • --fail-on high — exit 1 if any Critical OR High
  • --fail-on medium — exit 1 if Medium or above
  • --fail-on low — exit 1 on any finding (strict)

Recommended: start with --fail-on critical for the first month, then escalate to --fail-on high once the team is comfortable.

Inline Code Comments

The CLI's comment subcommand posts findings as PR review comments on the exact line of code. Developers see them in their IDE without leaving GitHub. Each comment includes:

  • Severity badge
  • Vulnerability class (e.g., "SQL Injection")
  • Why it's vulnerable
  • Suggested patch (AI-generated, copy-pasteable)

Baseline: Ignoring Pre-existing Findings

If your codebase has 200 existing findings and you don't want to block every PR, use the baseline feature:

bash
# Initial baseline guardianx baseline create --path . --output baseline.json # Subsequent scans ignore findings present in baseline guardianx scan sast --path . --baseline baseline.json --fail-on critical

New findings introduced in a PR will block; pre-existing ones won't.

DAST in CI/CD

For dynamic scanning, deploy your PR branch to a staging environment, then run:

bash
guardianx scan dast \ --url https://pr-${PR_NUMBER}.staging.acme.com \ --format json \ --output dast-findings.json \ --fail-on high

DAST scans take longer (2-10 min), so consider running them only on PRs touching specific paths (e.g., /api/).

Secrets Scanning

GuardianX also scans for hardcoded secrets (AWS keys, DB passwords, JWT secrets, Stripe keys):

bash
guardianx scan secrets --path . --fail-on any

Recommend: --fail-on any for secrets scanning — there's no acceptable number of leaked secrets.

Status Checks

After configuring the workflow, add GuardianX as a required status check in your branch protection rules (GitHub: Settings → Branches → Edit → Require status checks to pass before merging → select "GuardianX Security Scan").

Notifications

Wire findings to Slack:

bash
guardianx notify slack \ --findings findings.json \ --webhook ${{ secrets.SLACK_WEBHOOK }} \ --severity high

Performance Tips

  • Cache the GuardianX binary in CI to avoid re-downloading
  • Run SAST only on changed files with --diff flag for large repos
  • Parallelize SAST + DAST + secrets scans as separate jobs
  • Use a self-hosted runner for very large monorepos

Measuring DevSecOps Success

Track these metrics over time:

  • MTTR (mean time to remediate) — should drop
  • Critical findings per 1k LOC — should trend toward 0
  • % of PRs blocked by GuardianX — should be <5% after baseline
  • Time to first finding — should be <30 seconds

A mature CI/CD security pipeline catches 95%+ of vulnerabilities before merge, with zero developer friction. Sign up for GuardianX and try it on your repo today.

// Ready to ship secure code?

Sign up for GuardianX

Run a full SAST + DAST + patch-generation VAPT scan on your codebase in under 5 minutes. No credit card required.

// Keep reading

Related posts