Pensar
BlogCareersDocsApexSign inGet a demo
Back to blog
[GUIDES]2026-03-0610 min read

Set Up Continuous Pentesting in your CI: A 10-Minute Guide

You already use AI code review to catch quality issues. The missing layer is runtime validation: testing what actually happens when your application runs. Here's how to add continuous pentesting to your CI pipeline in 10 minutes, with configs for GitHub Actions, GitLab CI, and Bitbucket Pipelines.

Set Up Continuous Pentesting in CI: A 10-Minute Guide

Your pipeline already has layers. Linters catch style issues. Tests catch regressions. AI code review, whether it's Claude Code review or a homegrown solution, catches quality and security patterns in the diff.

But none of those layers run the application.

This matters more now than it did a year ago. Stripe's coding agents merge 1,300 PRs per week. Ramp's Inspect agent authors 30% of all merged PRs. Some engineers at Anthropic report 100% AI-written code. At that volume, "I'll review the diff" is not a security strategy. It's a coping mechanism for a pipeline you don't trust.

A coding agent opens a PR that adds a credit transfer endpoint. The code checks the sender's balance, validates it's sufficient, then executes the transfer. Every line is correct. The linter doesn't care. The tests pass because they run transfers sequentially. The AI reviewer sees proper validation logic. Static scanners won't flag this because there's nothing wrong with the code.

The bug is in the timing. At runtime, an attacker sends 10 concurrent transfer requests for their full balance. All 10 pass the balance check before any of them deduct: no row-level locking, no atomic transaction. $100 becomes $1,000. It's a textbook TOCTOU race condition, and it only exists when real requests hit a real database under concurrency. Runtime validation is the missing layer. With Pensar's CLI, adding it to your pipeline takes about 10 minutes.

Prerequisites

  • A Pensar account and API key (grab one from the Console under Settings → CI/CD)
  • A Pensar project ID (PENSAR_PROJECT_ID) unless you're running in GitHub Actions, where Pensar can auto-detect the repository ID
  • Node.js 22+
  • A CI pipeline you want to protect

Step 1: Test locally

Before touching CI config, verify the CLI works on your machine.

bash
npm install -g @pensar/ci

Set your API key and project ID:

bash
export PENSAR_API_KEY=your_key_here
export PENSAR_PROJECT_ID=your_project_id

Run a scan:

bash
pensar pentest --branch main

You'll get a scan ID and results in your terminal. By default, the CLI waits for the scan to finish and returns a non-zero exit code if the scan itself fails or is paused. Use your team's severity policy when you decide whether findings should block merges.

Check status on a running scan anytime:

bash
pensar status <scanId>

If that worked, you're ready for CI.

Step 2: Pick your pattern

Four patterns, each useful for a different stage. Most teams start with PR or push scans, then add post-deploy and scheduled coverage.

PatternTriggerSpeedCoverage
PR pentestPull request openedTargeted, minutesChanged attack surface
Push scanCommit pushedTargeted, minutesBranch changes
Post-deploySuccessful deploymentTargeted, minutesDeployed changes
Scheduled full scanCron (e.g., nightly)Comprehensive, longerEntire attack surface

Start with one. Add the others when you're ready.

GitHub Actions

PR pentest

Runs on every pull request targeting main. Pensar maps the attack surface from the diff and tests what changed.

yaml
name: Pensar PR Pentest
on:
pull_request:
branches: [main]
jobs:
pentest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
- run: npm install -g @pensar/ci
- run: pensar pentest --branch ${{ github.head_ref }} --commit ${{ github.event.pull_request.head.sha }}
env:
PENSAR_API_KEY: ${{ secrets.PENSAR_API_KEY }}

Add PENSAR_API_KEY to your repo secrets under Settings → Secrets and variables → Actions. If the repository is not connected to a Pensar project by repository ID, also add PENSAR_PROJECT_ID and pass it as an environment variable.

Push scan

Runs whenever code lands on your main branches. This is the simplest continuous monitoring setup for teams that want scans on branch updates rather than every PR.

yaml
name: Pensar Push Pentest
on:
push:
branches: [main, master, develop]
jobs:
pentest:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
- run: npm install -g @pensar/ci
- run: pensar pentest --branch ${{ github.ref_name }} --commit ${{ github.sha }}
env:
PENSAR_API_KEY: ${{ secrets.PENSAR_API_KEY }}

Post-deploy scan

Triggers after your deploy workflow completes. Validates the running application in its actual environment.

yaml
name: Pensar Post-Deploy Scan
on:
workflow_dispatch:
workflow_run:
workflows: ['Deploy']
types: [completed]
branches: [main]
jobs:
pentest:
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.workflow_run.head_sha || github.sha }}
- uses: actions/setup-node@v4
with:
node-version: '22'
- run: npm install -g @pensar/ci
- run: |
pensar pentest \
--branch ${{ github.event.workflow_run.head_branch || github.ref_name }} \
--commit ${{ github.event.workflow_run.head_sha || github.sha }}
env:
PENSAR_API_KEY: ${{ secrets.PENSAR_API_KEY }}

Replace 'Deploy' with whatever your deployment workflow is called. Keep checkout pinned to the same SHA you pass to --commit; checking out workflow_run.head_branch can scan code that was pushed after the deploy, and head_branch can be empty for tag-triggered deploys. If your infrastructure needs time to settle after deploy, add a short wait before the pensar pentest step.

Scheduled full scan

Comprehensive attack surface coverage on a schedule. Catches configuration drift, newly exploitable patterns, and anything the targeted scans missed.

yaml
name: Pensar Full Scan
on:
schedule:
- cron: '0 2 * * 1-5' # Weeknights at 2 AM
jobs:
pentest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
- run: npm install -g @pensar/ci
- run: pensar pentest --branch main
env:
PENSAR_API_KEY: ${{ secrets.PENSAR_API_KEY }}

GitLab CI

Add to your .gitlab-ci.yml:

yaml
pentest:
image: node:22
stage: test
script:
- npm install -g @pensar/ci
- pensar pentest --branch $CI_COMMIT_BRANCH
variables:
PENSAR_API_KEY: $PENSAR_API_KEY
PENSAR_PROJECT_ID: $PENSAR_PROJECT_ID
only:
- main
- merge_requests

Store PENSAR_API_KEY and PENSAR_PROJECT_ID in Settings → CI/CD → Variables. Mark the API key as masked and protected.

For post-deploy scanning, move the job to a later stage and add needs: [deploy] to chain it after your deployment job.

Bitbucket Pipelines

Add to bitbucket-pipelines.yml:

yaml
pipelines:
default:
- step:
name: Pensar Pentest
image: node:22
script:
- npm install -g @pensar/ci
- pensar pentest --branch $BITBUCKET_BRANCH

Add PENSAR_API_KEY and PENSAR_PROJECT_ID as repository variables under Repository settings → Pipelines → Repository variables.

For branch-specific triggers, use the branches key instead of default:

yaml
pipelines:
branches:
main:
- step:
name: Pensar Pentest
image: node:22
script:
- npm install -g @pensar/ci
- pensar pentest --branch main

Custom CI: Use the API

If you're on Jenkins, CircleCI, Buildkite, or anything else, Pensar's REST API works anywhere you can make HTTP requests.

Start a scan:

bash
curl -X POST https://api.pensar.dev/ci/dispatch \
-H "Authorization: Bearer $PENSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"projectId": "your-project-id",
"branch": "main",
"scanLevel": "priority"
}'

Returns a scanId.

Check status:

bash
curl https://api.pensar.dev/ci/status/$SCAN_ID \
-H "Authorization: Bearer $PENSAR_API_KEY"

Returns scan metadata including status, issue count, progress, and whether the report is ready. Wire these two calls into any CI system with a simple shell script.

Step 3: Graduate from informing to gating

Start non-blocking. Let pentest results flow to PRs and dashboards for a few weeks while your team calibrates on what's signal and what's noise.

When you trust it, make it a hard gate:

GitHub: Settings → Branches → Branch protection rules → Require status checks → add Pensar PR Pentest.

GitLab: Set allow_failure: false on the pentest job (this is the default).

Bitbucket: Repository settings → Branch permissions → add the pentest step as a required check.

Now the Pensar check becomes part of the same release decision as tests, linting, and build. Start by requiring the job to complete successfully, then tighten the failure policy around the severities your team wants to block.

Make the green check mean something

Your CI green check should give you real confidence to merge, especially when the code was written by an agent and nobody's reading the diff line by line.

Here's what a modern CI pipeline looks like with all layers in place:

PR opened
  ├── Tests & linting              (deterministic correctness)
  ├── AI code review               (pattern-level quality + security)
  ├── Static security analysis     (known vulnerability patterns in source)
  └── Pensar pentest               (adversarial verification at runtime)
       └── clean or below policy threshold → merge
       └── confirmed finding above policy threshold → block + investigate

The first three layers analyze code. The last layer runs it and tries to break in. It maps your attack surface from the diff, chains findings across your actual running application, and proves what's exploitable before the code ships. That's the layer that catches auth bypasses, business logic flaws, race conditions, and cross-layer architectural bugs that only exist at runtime.

You already trust CI to tell you if your tests pass. Now it can tell you if that agent-written PR introduces security issues and pass along all the context needed to patch them.

FAQ

What is continuous pentesting and how is it different from traditional security testing?

Continuous pentesting is runtime validation that tests what actually happens when your application runs, catching vulnerabilities like race conditions and business logic flaws that only exist under real execution. Unlike static analysis or code review which examine source code, it runs adversarial tests against your live application to prove what's exploitable.

How long does it take to set up continuous pentesting in CI?

You can add continuous pentesting to your CI pipeline in about 10 minutes by installing the Pensar CLI, setting your API key, and adding a simple workflow configuration to GitHub Actions, GitLab CI, or Bitbucket Pipelines.

What are the main patterns for running continuous pentesting?

The main patterns are PR pentests (triggered on pull requests), push scans (triggered when commits land on protected branches), post-deploy scans (triggered after successful deployments), and scheduled full scans (comprehensive nightly or weekly scans covering the entire attack surface).

Should I make pentesting a blocking check immediately?

No, start non-blocking and let results flow to PRs and dashboards for a few weeks while your team calibrates on signal versus noise. Once you trust it, graduate to a required check and tune the failure policy around the severities your team wants to block.

What prerequisites do I need to add continuous pentesting to my pipeline?

You need a Pensar account with an API key from the Console, a Pensar project ID unless your CI environment can be matched by repository ID, Node.js 22 or higher, and an existing CI pipeline on GitHub Actions, GitLab CI, Bitbucket Pipelines, or any system that can make HTTP requests.

Why is runtime validation important when using AI coding agents?

AI agents at companies like Stripe and Ramp now generate hundreds or thousands of PRs per week, making manual code review impractical. Runtime validation catches bugs like race conditions and concurrency issues that look correct in code but only manifest when the application actually runs under real conditions.

Can I use Pensar with CI systems other than GitHub, GitLab, or Bitbucket?

Yes, Pensar provides a REST API that works with any CI system including Jenkins, CircleCI, and Buildkite. You can trigger scans and check status using simple HTTP requests from shell scripts.

Share this article

Kerem Proulx

Written by

Kerem Proulx

Pensar

Continuous adversarial testing.
Born and raised in NYC.

[email protected]
Product
Apex
Resources
DocumentationBlog
Company
CareersTermsPrivacySubprocessors
© PensarAI, Inc. 2026ALL RIGHTS RESERVED