---
title: "GitHub Action for CI/CD Pentesting"
description: "Learn how to integrate TurboPentest into your CI/CD pipeline with a GitHub Action that triggers pentests automatically on every deployment."
canonical: https://turbopentest.com/learn/integration-automation/github-action
source: "TurboPentest Learn"
---

# 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, fail the step if the pentest fails, or fail when findings meet a severity threshold via fail-on.

### Basic Workflow Configuration

A minimal workflow file (`.github/workflows/pentest.yml`) looks like this:

```yaml
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: true
          fail-on: high
```

### Configuration 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 (Secret Scanner, Code Scanner, and Dep Scanner 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.
- **fail-on** (optional, default empty): Fail the workflow when any finding severity meets or exceeds this threshold (`critical`, `high`, or `medium`). Empty, `off`, or `none` means report-only (exit 0). Severity order is critical > high > medium > low > info, so `fail-on: high` fails on high or critical. Requires `wait-for-results: true` (the action errors if `fail-on` is set without waiting).
- **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.

```yaml
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`:

```yaml
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 also gate promotion on finding severity, set `fail-on` to `critical`, `high`, or `medium`. When the pentest completes, the action fetches findings from `GET /api/pentests/{id}` and exits non-zero (with a `::error::` summary) if any finding meets or exceeds that threshold.

```yaml
- 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
    fail-on: high

- name: Promote to Production
  if: success()
  run: ./deploy-production.sh
```

If any high or critical vulnerabilities are found, the action fails and the promotion step is skipped. The pentest URL is printed in the action output for immediate review. `fail-on` without `wait-for-results: true` is rejected so misconfigured workflows fail fast instead of silently shipping.

### 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-results` is true (`complete`, `failed`, or `timeout`)

### 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 (Secret Scanner, Code Scanner, Dep Scanner) alongside the dynamic pentest, giving you both runtime and code-level coverage.
