CI/CD Integration
Integrate TurboPentest into your CI/CD pipeline to automatically run a pentest on every deployment (or on a schedule). This is the highest-value use case - continuous security coverage with zero manual effort.
Prerequisites
- An API key (create one in Dashboard > API Keys)
- Your domain verified in TurboPentest
- Pentest credits available (use a subscription for continuous coverage)
GitHub Actions
name: Security Pentest
on:
push:
branches: [main]
jobs:
pentest:
runs-on: ubuntu-latest
steps:
- name: Start TurboPentest
run: |
RESPONSE=$(curl -sf -X POST https://turbopentest.com/api/pentests \
-H "X-API-Key: ${{ secrets.TURBOPENTEST_API_KEY }}" \
-H "Content-Type: application/json" \
-d '{"targetUrl": "${{ vars.TARGET_URL }}"}')
SCAN_ID=$(echo $RESPONSE | jq -r '.id')
echo "SCAN_ID=$SCAN_ID" >> $GITHUB_ENV
echo "Pentest started: $SCAN_ID"
- name: Wait for results
run: |
for i in $(seq 1 120); do
STATUS=$(curl -sf -H "X-API-Key: ${{ secrets.TURBOPENTEST_API_KEY }}" \
https://turbopentest.com/api/pentests/$SCAN_ID | jq -r '.status')
if [ "$STATUS" = "complete" ]; then
echo "Pentest complete"
break
fi
if [ "$STATUS" = "failed" ]; then
echo "Pentest failed"
exit 1
fi
echo "Status: $STATUS - waiting 60s..."
sleep 60
done
- name: Check for critical findings
run: |
CRITICALS=$(curl -sf -H "X-API-Key: ${{ secrets.TURBOPENTEST_API_KEY }}" \
https://turbopentest.com/api/pentests/$SCAN_ID | \
jq '[.findings[] | select(.severity == "critical")] | length')
if [ "$CRITICALS" -gt 0 ]; then
echo "Found $CRITICALS critical findings - failing build"
exit 1
fi
echo "No critical findings"Setup: Add TURBOPENTEST_API_KEY as a repository secret and TARGET_URL as a repository variable.
GitLab CI/CD
pentest:
stage: test
image: alpine:latest
before_script:
- apk add --no-cache curl jq
script:
- |
RESPONSE=$(curl -sf -X POST https://turbopentest.com/api/pentests \
-H "X-API-Key: $TURBOPENTEST_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"targetUrl\": \"$TARGET_URL\"}")
SCAN_ID=$(echo $RESPONSE | jq -r '.id')
echo "Pentest started: $SCAN_ID"
for i in $(seq 1 120); do
STATUS=$(curl -sf -H "X-API-Key: $TURBOPENTEST_API_KEY" \
https://turbopentest.com/api/pentests/$SCAN_ID | jq -r '.status')
if [ "$STATUS" = "complete" ]; then break; fi
if [ "$STATUS" = "failed" ]; then exit 1; fi
sleep 60
done
CRITICALS=$(curl -sf -H "X-API-Key: $TURBOPENTEST_API_KEY" \
https://turbopentest.com/api/pentests/$SCAN_ID | \
jq '[.findings[] | select(.severity == "critical")] | length')
if [ "$CRITICALS" -gt 0 ]; then exit 1; fi
only:
- mainSetup: Add TURBOPENTEST_API_KEY and TARGET_URL as CI/CD variables.
Jenkins
pipeline {
agent any
environment {
TURBOPENTEST_API_KEY = credentials('turbopentest-api-key')
TARGET_URL = 'https://app.example.com'
}
stages {
stage('Pentest') {
steps {
sh '''
RESPONSE=$(curl -sf -X POST https://turbopentest.com/api/pentests \
-H "X-API-Key: $TURBOPENTEST_API_KEY" \
-H "Content-Type: application/json" \
-d "{\\"targetUrl\\": \\"$TARGET_URL\\"}")
SCAN_ID=$(echo $RESPONSE | jq -r '.id')
echo "Pentest started: $SCAN_ID"
for i in $(seq 1 120); do
STATUS=$(curl -sf -H "X-API-Key: $TURBOPENTEST_API_KEY" \
https://turbopentest.com/api/pentests/$SCAN_ID | jq -r '.status')
if [ "$STATUS" = "complete" ]; then break; fi
if [ "$STATUS" = "failed" ]; then exit 1; fi
sleep 60
done
CRITICALS=$(curl -sf -H "X-API-Key: $TURBOPENTEST_API_KEY" \
https://turbopentest.com/api/pentests/$SCAN_ID | \
jq '[.findings[] | select(.severity == "critical")] | length')
if [ "$CRITICALS" -gt 0 ]; then exit 1; fi
'''
}
}
}
}CircleCI
version: 2.1
jobs:
pentest:
docker:
- image: cimg/base:current
steps:
- run:
name: Run TurboPentest
command: |
RESPONSE=$(curl -sf -X POST https://turbopentest.com/api/pentests \
-H "X-API-Key: $TURBOPENTEST_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"targetUrl\": \"$TARGET_URL\"}")
SCAN_ID=$(echo $RESPONSE | jq -r '.id')
for i in $(seq 1 120); do
STATUS=$(curl -sf -H "X-API-Key: $TURBOPENTEST_API_KEY" \
https://turbopentest.com/api/pentests/$SCAN_ID | jq -r '.status')
if [ "$STATUS" = "complete" ]; then break; fi
if [ "$STATUS" = "failed" ]; then exit 1; fi
sleep 60
done
CRITICALS=$(curl -sf -H "X-API-Key: $TURBOPENTEST_API_KEY" \
https://turbopentest.com/api/pentests/$SCAN_ID | \
jq '[.findings[] | select(.severity == "critical")] | length')
if [ "$CRITICALS" -gt 0 ]; then exit 1; fi
workflows:
deploy-and-test:
jobs:
- pentest:
filters:
branches:
only: mainAzure DevOps
trigger:
branches:
include:
- main
pool:
vmImage: 'ubuntu-latest'
steps:
- script: |
RESPONSE=$(curl -sf -X POST https://turbopentest.com/api/pentests \
-H "X-API-Key: $(TURBOPENTEST_API_KEY)" \
-H "Content-Type: application/json" \
-d "{\"targetUrl\": \"$(TARGET_URL)\"}")
SCAN_ID=$(echo $RESPONSE | jq -r '.id')
for i in $(seq 1 120); do
STATUS=$(curl -sf -H "X-API-Key: $(TURBOPENTEST_API_KEY)" \
https://turbopentest.com/api/pentests/$SCAN_ID | jq -r '.status')
if [ "$STATUS" = "complete" ]; then break; fi
if [ "$STATUS" = "failed" ]; then exit 1; fi
sleep 60
done
CRITICALS=$(curl -sf -H "X-API-Key: $(TURBOPENTEST_API_KEY)" \
https://turbopentest.com/api/pentests/$SCAN_ID | \
jq '[.findings[] | select(.severity == "critical")] | length')
if [ "$CRITICALS" -gt 0 ]; then exit 1; fi
displayName: 'Run TurboPentest'Common patterns
Fail on critical or high findings
Replace the critical check with:
SERIOUS=$(curl -sf -H "X-API-Key: $TURBOPENTEST_API_KEY" \
https://turbopentest.com/api/pentests/$SCAN_ID | \
jq '[.findings[] | select(.severity == "critical" or .severity == "high")] | length')
if [ "$SERIOUS" -gt 0 ]; then exit 1; fiSchedule pentests (not on every commit)
Use your CI/CD platform's cron/schedule feature instead of on: push. For example in GitHub Actions:
on:
schedule:
- cron: '0 2 * * 1' # Every Monday at 2amWhite box pentests in CI/CD
Add repoUrl to the request body:
-d '{
"targetUrl": "'"$TARGET_URL"'",
"repoUrl": "https://github.com/'"$GITHUB_REPOSITORY"'"
}'