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 — 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 — and 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)
- 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.
npm install -g @pensar/ci
Set your API key:
export PENSAR_API_KEY=your_key_here
Run a scan:
pensar pentest --environment dev
You'll get a scan ID and results in your terminal. Exit code 0 means clean. Exit code 1 means findings — this is what CI will use to gate merges later.
Check status on a running scan anytime:
pensar status <scanId>
If that worked, you're ready for CI.
Step 2: Pick your pattern
Three patterns, each useful for different stages. Most teams end up running all three.
| Pattern | Trigger | Speed | Coverage |
|---|---|---|---|
| PR pentest | Pull request opened | Targeted — minutes | Changed attack surface |
| Post-deploy | Successful deployment | Targeted — minutes | Deployed changes |
| Scheduled full scan | Cron (e.g., nightly) | Comprehensive — longer | Entire 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.
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 }} --environment staging
env:
PENSAR_API_KEY: ${{ secrets.PENSAR_API_KEY }}
Add PENSAR_API_KEY to your repo secrets under Settings → Secrets and variables → Actions. That's it.
Post-deploy scan
Triggers after your deploy workflow completes. Validates the running application in its actual environment.
name: Pensar Post-Deploy Scan
on:
workflow_run:
workflows: ['Deploy']
types: [completed]
branches: [main]
jobs:
pentest:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
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 }} --environment staging
env:
PENSAR_API_KEY: ${{ secrets.PENSAR_API_KEY }}
Replace 'Deploy' with whatever your deployment workflow is called.
Scheduled full scan
Comprehensive attack surface coverage on a schedule. Catches configuration drift, newly exploitable patterns, and anything the targeted scans missed.
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 --environment production
env:
PENSAR_API_KEY: ${{ secrets.PENSAR_API_KEY }}
GitLab CI
Add to your .gitlab-ci.yml:
pentest:
image: node:22
stage: test
script:
- npm install -g @pensar/ci
- pensar pentest --branch $CI_COMMIT_BRANCH --environment staging
variables:
PENSAR_API_KEY: $PENSAR_API_KEY
only:
- main
- merge_requests
Store PENSAR_API_KEY in Settings → CI/CD → Variables. Mark it 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:
pipelines:
default:
- step:
name: Pensar Pentest
image: node:22
script:
- npm install -g @pensar/ci
- pensar pentest --branch $BITBUCKET_BRANCH --environment staging
Add PENSAR_API_KEY as a repository variable under Repository settings → Pipelines → Repository variables.
For branch-specific triggers, use the branches key instead of default:
pipelines:
branches:
main:
- step:
name: Pensar Pentest
image: node:22
script:
- npm install -g @pensar/ci
- pensar pentest --branch main --environment staging
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:
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:
curl https://api.pensar.dev/ci/status/$SCAN_ID \
-H "Authorization: Bearer $PENSAR_API_KEY"
Returns scan metadata including status, issue count, and a link to the full report. 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 no PR merges to main with a confirmed vulnerability. Exit code 1 blocks the merge. Exit code 0 lets it through.
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)
└── exit 0 → merge
└── exit 1 → block + findings in PR
The first three layers analyze code. The last layer runs it and tries to break in — an adversary that 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 you can trust it to 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 three main patterns for running continuous pentesting?
The three patterns are PR pentests (triggered on pull requests, testing changed attack surface in minutes), post-deploy scans (triggered after successful deployments to validate the running environment), 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 making it a hard gate that blocks merges when vulnerabilities are found.
What prerequisites do I need to add continuous pentesting to my pipeline?
You need a Pensar account with an API key from the Console, 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.