UnveilTech

UnveilScan Blog

← All articles

Try UnveilScan free

CI integration: blocking PRs on score regression

Posted 2026-04-29 · 6 min read · devopsCI

Security findings are a moving target. A clean main branch today doesn't help if a Friday merge introduces a CSP regression you don't notice until next quarter. The fix is to scan in CI and block PRs that would degrade your security posture.

UnveilScan ships a CLI binary (unveilscan-cli) and a packaged GitHub Action that does this in 6 lines of YAML. Here's the setup.

The CLI

The CLI launches a scan via your Bearer API token, polls until completion, and exits with a code based on the worst severity found:

$ unveilscan-cli -domain example.com -token $UNVEILSCAN_TOKEN \
                  -profile basic -fail-on high

✔ Scan completed: 89/100 (A)
✔ 0 critical, 0 high, 2 medium, 5 low findings
$ echo $?
0

Exit codes:

Exit codeMeaning
0Scan succeeded; nothing at or above -fail-on severity
1Scan succeeded but found findings at or above -fail-on
2API or network error (couldn't reach UnveilScan)
3CLI argument invalid (bad domain, missing token)

Most CIs interpret a non-zero exit as failure. Plug into .gitlab-ci.yml, Jenkins pipelines, CircleCI, etc. — same pattern.

The GitHub Action

Drop this in .github/workflows/security.yml:

name: UnveilScan
on:
  pull_request:
    branches: [main]

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: unveiltech/unveilscan-action@v1
        with:
          domain: example.com
          token: ${{ secrets.UNVEILSCAN_TOKEN }}
          profile: extended
          fail-on: high

On every PR the Action runs an Extended scan against your production domain. If any finding is HIGH or worse, the check fails. The PR can't merge (assuming you required the check in branch protection rules).

What "production domain" means in this context

The first wrinkle: you can't scan code that hasn't shipped yet. Two options:

  1. Scan staging. Most teams have a staging.example.com that gets the PR's code via a Vercel preview / Netlify preview / dedicated CI environment. Point the Action at staging.
  2. Scan main after deploy. The Action runs on push to main, blocks the deploy if it'd worsen the posture. Closer to "tripwire" than "gate".

Best with both: PR scan against staging (catches regressions early) + main scan as tripwire (catches what slipped through staging differences).

Common configuration patterns

Strict — fail on any new HIGH

fail-on: high

Permissive — only fail on regression

You want to ship a new feature even if it has 1 LOW finding. Block only if the score drops compared to baseline. Requires a custom step:

- uses: unveiltech/unveilscan-action@v1
  with:
    domain: example.com
    token: ${{ secrets.UNVEILSCAN_TOKEN }}
    output: json
    output-path: scan.json

- name: Compare score
  run: |
    SCORE=$(jq .score.global scan.json)
    BASELINE=85   # set per-team
    if [ "$SCORE" -lt "$BASELINE" ]; then
      echo "Score $SCORE below baseline $BASELINE"
      exit 1
    fi

Active scan in CI

- uses: unveiltech/unveilscan-action@v1
  with:
    domain: staging.example.com
    token: ${{ secrets.UNVEILSCAN_TOKEN }}
    profile: active
    ack-active: true       # required gate
    fail-on: high

Triple-gated (ownership + ack + non-destructive probes — see CLAUDE.md §2). Costs a license slot per domain on UnveilScan but covers CVE probing.

Token management

Generate a Bearer token from /me/tokens in the UnveilScan SPA. Scope: Bearer tokens currently scope to "all the user's owned domains" (no per-domain scoping yet — coming in v2). Treat the token like a deploy key:

Cost considerations

Every PR triggers a scan. On the Free tier (3 Extended scans / month) you'll exhaust the budget after 3 PRs. For active CI use, a license is the right model — it gives unlimited Extended scans on pinned domains. Pricing.

Reading the output

Job logs show a structured summary. For richer diff (which findings appeared / were fixed since last scan), the JSON output includes a delta block when compared against the previous scan on the same domain.

Set up CI scanning

UnveilScan CLI + GitHub Action. License covers unlimited CI scans on pinned domains.

See pricing