GitHub Action for CI/CD Pentesting
Why CI/CD Security Testing Matters
Traditional penetration testing happens periodically - quarterly, annually, or before a major release. But modern development teams push code to production multiple times per day. A vulnerability introduced on Monday morning could be exploited long before a quarterly pentest discovers it.
CI/CD-integrated pentesting closes this gap. By triggering a pentest on every deployment (or every merge to a protected branch), you ensure that no code reaches production without a security assessment.
The TurboPentest GitHub Action
TurboPentest provides an official GitHub Action (integsec/turbopentest-action) that can be added to any GitHub Actions workflow. It authenticates with your TurboPentest API key, launches a pentest against the specified target, and can optionally wait for the result and fail the step if the pentest fails.
Basic Workflow Configuration
A minimal workflow file (.github/workflows/pentest.yml) looks like this:
name: Security Pentest
on:
push:
branches: [main]
deployment_status:
jobs:
pentest:
runs-on: ubuntu-latest
if: github.event.deployment_status.state == 'success' || github.event_name == 'push'
steps:
- name: Run TurboPentest
uses: integsec/turbopentest-action@v1
with:
api-key: ${{ secrets.TURBOPENTEST_API_KEY }}
target-url: "https://staging.example.com"
wait-for-results: trueConfiguration Parameters
The action accepts the following inputs:
- api-key (required): Your TurboPentest API key, stored as a GitHub secret
- target-url (required): The URL to pentest. This should point to a staging or preview environment, never production during active user traffic. The domain must be verified in your TurboPentest account.
- repo-url (optional): A GitHub repository URL to enable white-box pentesting (gitleaks, semgrep, and grype run against the source)
- wait-for-results (optional, default
false): Whether the action should poll until the pentest completes (every 30 seconds, up to 2 hours) before continuing. The step fails if the pentest fails. - api-base-url (optional, default
https://turbopentest.com): Override for testing against a non-production API
The pentest tier is determined by the credit consumed: the API uses your oldest available credit, so keep credits of the tier you want (or launch via the API directly with the tier field for explicit control).
Triggering on Deployment
The most effective pattern triggers the pentest after a successful deployment to a staging environment. This way, the pentest runs against the actual deployed application (not just the code), and deployment failures do not waste pentest credits.
on:
deployment_status:
jobs:
pentest:
if: github.event.deployment_status.state == 'success'For preview environments (Vercel, Netlify, etc.), the deployment URL is available as github.event.deployment_status.target_url:
with:
target-url: ${{ github.event.deployment_status.target_url }}Gating Deployments
With wait-for-results: true, the step fails when the pentest itself fails, which blocks any subsequent steps. To gate promotion on finding severity, add a follow-up step that fetches the results via the REST API (using the pentest-id output) and exits non-zero when findings at or above your threshold are present:
- name: Run TurboPentest
id: pentest
uses: integsec/turbopentest-action@v1
with:
api-key: ${{ secrets.TURBOPENTEST_API_KEY }}
target-url: ${{ env.STAGING_URL }}
wait-for-results: true
- name: Gate on high severity findings
run: |
HIGH=$(curl -sf "https://turbopentest.com/api/pentests/${{ steps.pentest.outputs.pentest-id }}" \
-H "Authorization: Bearer ${{ secrets.TURBOPENTEST_API_KEY }}" \
| jq '[.findings[] | select(.severity == "critical" or .severity == "high")] | length')
if [ "$HIGH" -gt 0 ]; then
echo "::error::$HIGH high or critical findings - blocking promotion"
exit 1
fi
- name: Promote to Production
if: success()
run: ./deploy-production.shIf any high or critical vulnerabilities are found, the promotion step is skipped and the pipeline fails. The pentest URL is printed in the action output for immediate review.
Action Outputs
The action provides outputs for use in subsequent steps:
- pentest-id: The unique identifier for referencing via API
- pentest-url: Direct link to the pentest results in TurboPentest
- status: The final pentest status when
wait-for-resultsis true (complete,failed, ortimeout)
Best Practices
Use staging environments. Never pentest production during peak traffic. The action works best against staging or preview deployments that mirror production configuration.
Store API keys as secrets. Never hardcode your TurboPentest API key in the workflow file. Use GitHub's encrypted secrets feature.
Choose the right tier. Use recon credits for fast feedback on every PR. Use standard or deep for merges to main. Reserve blitz for release candidates.
Set appropriate thresholds. Start by gating only on critical findings and tighten to high as your team remediates existing findings.
Combine with source analysis. Pass repo-url to enable the white-box tools (gitleaks, semgrep, grype) alongside the dynamic pentest, giving you both runtime and code-level coverage.
Course 6: Integration & Automation
Learn how to embed TurboPentest into your development workflows with CI/CD pipelines, AI-assistant integrations, notification systems, REST APIs, and scheduled pentesting.
MCP Server for AI-Assistant-Driven Pentesting
Learn how to use TurboPentest's MCP Server to launch and manage pentests directly from AI-powered code editors and assistants.