CI/CD Integration
Overview
Pensar provides a CLI tool and API for integrating automated pentesting directly into your CI/CD pipeline. Run pentests on every commit or deployment to catch vulnerabilities before they reach production.
CI-triggered pentests consume credits from your workspace balance. See Billing & Usage for details on credit management and usage tracking.
Installation
Install the Pensar CI package globally:
Or add it as a dev dependency:
View the source code and additional examples on GitHub.
Configuration
Required Environment Variables
Optional Environment Variables
You can configure CI/CD integration directly from the Pensar Console. Navigate to Settings → Automation → CI/CD in your workspace for guided setup and one-click workflow creation.
CLI Usage
Run a Pentest
Options:
Examples:
How the CI diff is calculated
By default:
- Compare: Your requested commit (
--commit) is compared with the latest qualifying completed pentest for the same repository in your workspace. This baseline is shared across branches and environments; it is not the previous deployment. - Filter: Files such as
package.jsonand lockfiles are excluded. If no changes remain, testing is skipped; large changes can trigger broader coverage. - Test: The agent uses the diff to create security objectives for the affected applications.
- Results: A launched scan returns an ID and status, with findings after testing completes. Buffered runs can defer launch; a queued scan is not a passing result.
Without a baseline, or if a force-push removed it, Pensar runs without diff narrowing. Other comparison errors fail the run. For an explicit comparison, the API accepts baseSha with commitSha; both commits must exist.
Compare two explicit commits
To test exactly what changed between two deployed versions, pass the previously deployed commit as --base (or PENSAR_BASE_SHA) along with the new commit. Requires @pensar/ci 2.7.0 or later.
In GitHub Actions the repository is detected automatically; elsewhere, pass --repo-id. The CLI sends these as baseSha and commitSha to POST /ci/dispatch, which you can also call directly.
- Both values must be full 40-character SHAs, and the repository must be known (
--repo-id, orrepoId/repositoryIdon the API). - Both commits must exist in that repository. A missing commit returns
400; Pensar does not fall back to the last pentest or a full scan. - The two commit trees are compared directly, so rollbacks and diverged histories work. The commit buffer is bypassed.
- If the two commits are the same, there is nothing to test and the run is skipped.
- The range is kept if the scan is queued, deduplicated, or retried.
Run Only on Labelled Changes
When only some changes are worth pentesting, label the pull request and gate the workflow on that label. This is done in the workflow, not in the CLI.
On a pull-request-triggered workflow the labels are in the event payload, so a job-level condition is all you need and nothing extra runs:
On a push or a post-deploy run there is no pull request in the payload, so the pull request the commit came from has to be looked up. Add a small job that does the lookup and make the pentest job depend on it:
Because the pentest job depends on the gate, an unlabelled change never starts it: no runner, no checkout, no install.
Keep the label in LABEL rather than inlining it into the command. A label is
free text, so an apostrophe would break the quoting and a . or * would make
grep match a different label; -F compares it literally.
After a deploy, use ${{ github.event.workflow_run.head_sha }} rather than
${{ github.sha }}. On a workflow_run event github.sha is the tip of the
default branch, not the commit that was deployed, so the gate would check
whichever change landed most recently.
The Console generates both of these for you: enter a label under Settings → CI/CD and the workflow snippets on that page include the gate, with the right SHA for each trigger.
Check Scan Status
Example:
CI/CD Examples
GitHub Actions
On Push
After Deployment
Compare With Last Deploy
Run a pentest when commits are pushed to main branches:
GitLab CI
Add to your .gitlab-ci.yml:
Bitbucket Pipelines
Add to your bitbucket-pipelines.yml:
Exit Codes
By default, the CLI will exit with code 1 if any security issues are found. Use this to block deployments when vulnerabilities are detected.
API Reference
Dispatch Scan
Headers:
Authorization: Bearer <api_key>orx-api-key: <api_key>Content-Type: application/json
Request Body:
Provide a repository so the pentest is scoped to that repository’s
applications. Supply either repoId (GitHub’s numeric repository id — the
CLI auto-detects this from GITHUB_REPOSITORY_ID) or repositoryId (the
internal repository UUID). When neither is supplied, application scope remains
workspace-wide. Exactly one connected repository still supplies attribution and
a baseline; multiple repositories leave the scan unattributed without diff narrowing.
The repository must already be connected to the workspace. If it isn’t found,
the API responds 404; if it belongs to another workspace, 403; if it has
no discovered applications yet (run reconnaissance first), 422.
With baseSha, CI compares exactly that commit to commitSha in the selected repository and bypasses the commit buffer. Both commits must exist there; a missing commit returns 400 instead of recovering to a full scan. Omit baseSha to keep the default last-pentest baseline.
testType: custom derives application-level objectives from the diff;
endpoint-selection selects affected discovered endpoints. Both use the same
repository baseline and recovery rules above. A supplied branch must match the
repository’s configured target branch; it does not select a different branch for one run.
Response:
Get Scan Status
Headers:
Authorization: Bearer <api_key>orx-api-key: <api_key>
Response: