Integrate security scanning into GitHub Actions, GitLab CI, or Jenkins to block insecure deployments.
Shift security left by integrating automated security scanning directly into your CI/CD pipeline. This tutorial shows you how to use reNgine Cloud API to scan staging environments, block deployments with critical vulnerabilities, and track security debt over time.
Use the reNgine Cloud API to trigger scans automatically when code is pushed to staging or production branches. Store your API key as a CI/CD secret for secure authentication.
# GitHub Actions Workflow
name: Security Scan
on:
push:
branches: [staging, main]
pull_request:
branches: [main]
jobs:
security_scan:
runs-on: ubuntu-latest
steps:
- name: Trigger reNgine Scan
env:
RENGINE_API_KEY: ${{ secrets.RENGINE_API_KEY }}
RENGINE_URL: ${{ secrets.RENGINE_URL }}
run: |
SCAN_ID=$(curl -X POST "$RENGINE_URL/api/scans" \
-H "Authorization: Bearer $RENGINE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"target": "staging.example.com",
"scan_type": "full",
"notify_on_completion": true
}' | jq -r '.scan_id')
echo "SCAN_ID=$SCAN_ID" >> $GITHUB_ENV
echo "Started scan: $SCAN_ID"
Poll the scan status API until completion, then check for critical or high-severity vulnerabilities. Fail the pipeline if security thresholds are exceeded.
- name: Wait for Scan Completion
run: |
while true; do
STATUS=$(curl -s "$RENGINE_URL/api/scans/$SCAN_ID/status" \
-H "Authorization: Bearer $RENGINE_API_KEY" \
| jq -r '.status')
if [ "$STATUS" = "completed" ]; then
break
fi
echo "Scan status: $STATUS. Waiting 30s..."
sleep 30
done
- name: Check Security Findings
run: |
RESULTS=$(curl -s "$RENGINE_URL/api/scans/$SCAN_ID/results" \
-H "Authorization: Bearer $RENGINE_API_KEY")
CRITICAL=$(echo $RESULTS | jq '.findings.critical // 0')
HIGH=$(echo $RESULTS | jq '.findings.high // 0')
echo "Critical: $CRITICAL, High: $HIGH"
# Fail if critical findings exist
if [ "$CRITICAL" -gt 0 ]; then
echo "❌ CRITICAL vulnerabilities found! Blocking deployment."
exit 1
fi
# Warn if high findings exist
if [ "$HIGH" -gt 3 ]; then
echo "⚠️ Warning: $HIGH HIGH severity findings detected"
exit 1
fi
echo "✅ Security scan passed"
For microservices architectures, scan multiple staging endpoints in parallel to reduce total pipeline time. Use matrix builds to scale across services.
stages:
- deploy
- security
deploy_staging:
stage: deploy
script:
- deploy_to_staging.sh
environment:
name: staging
security_scan:
stage: security
parallel:
matrix:
- SERVICE: [api, web, admin, mobile-api]
script:
- |
curl -X POST "$RENGINE_URL/api/scans" \
-H "Authorization: Bearer $RENGINE_API_KEY" \
-d "{\"target\": \"$SERVICE.staging.example.com\"}"
only:
- staging
- main
Create visual security status badges for your README to communicate security posture to stakeholders. Use shields.io or generate custom SVG badges via API.
- name: Generate Security Badge
run: |
SCORE=$(curl -s "$RENGINE_URL/api/scans/$SCAN_ID/score" \
-H "Authorization: Bearer $RENGINE_API_KEY" \
| jq -r '.security_score')
# Determine badge color
if [ "$SCORE" -gt 90 ]; then COLOR="brightgreen"
elif [ "$SCORE" -gt 70 ]; then COLOR="yellow"
else COLOR="red"; fi
# Generate badge URL
BADGE_URL="https://img.shields.io/badge/security-$SCORE%25-$COLOR"
echo "" >> security-badge.md
- name: Update README
run: |
# Replace security badge in README
sed -i "s|!\[Security\].*|$(cat security-badge.md)|" README.md
- name: Commit Badge Update
uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: "Update security badge [skip ci]"
file_pattern: README.md
Store scan results as CI/CD artifacts and track security metrics across deployments. Use dashboards to visualize trends and measure security improvement.
pipeline {
agent any
stages {
stage('Deploy to Staging') {
steps {
sh 'deploy_staging.sh'
}
}
stage('Security Scan') {
steps {
script {
def scanId = sh(
script: """curl -X POST ${env.RENGINE_URL}/api/scans \
-H 'Authorization: Bearer ${env.RENGINE_API_KEY}' \
-d '{"target": "staging.example.com"}' \
| jq -r '.scan_id'""",
returnStdout: true
).trim()
env.SCAN_ID = scanId
}
}
}
stage('Wait & Validate') {
steps {
script {
timeout(time: 30, unit: 'MINUTES') {
waitUntil {
def status = sh(
script: "curl -s ${env.RENGINE_URL}/api/scans/${env.SCAN_ID}/status | jq -r '.status'",
returnStdout: true
).trim()
return status == 'completed'
}
}
def critical = sh(
script: "curl -s ${env.RENGINE_URL}/api/scans/${env.SCAN_ID}/results | jq '.findings.critical'",
returnStdout: true
).trim().toInteger()
if (critical > 0) {
error("Critical vulnerabilities found! Deployment blocked.")
}
}
}
}
}
post {
always {
archiveArtifacts artifacts: 'security-report.json', allowEmptyArchive: true
publishHTML([
reportDir: 'security',
reportFiles: 'report.html',
reportName: 'Security Scan Report'
])
}
}
}