Penetration Testing Automation: Tools and Workflows for Continuous Security

Penetration Testing Automation: Tools and Workflows for Continuous Security

Penetration testing automation runs security checks continuously and repeatably rather than as annual point-in-time assessments. Tools like Nuclei (vulnerability templates), Nmap (port scanning), SQLMap (SQL injection), and Nikto (web server scanning) can be combined into a pipeline that runs on every deployment. This guide covers how to build that pipeline for authorized environments.

What Penetration Testing Automation Is (and Isn't)

Automated penetration testing is not a replacement for manual penetration testing by skilled security professionals. It's a tool for:

  • Regression testing — verify that known vulnerability patterns aren't reintroduced after fixes
  • Continuous monitoring — catch obvious security misconfigurations as soon as they're introduced
  • Pre-pentest reconnaissance — identify easy targets before manual testing focuses on complex logic issues

Important: Automated tools have high false positive rates and miss complex vulnerabilities (business logic flaws, authentication bypasses, chained exploits). They find known patterns; they don't find novel vulnerabilities.

Authorization requirement: Never run these tools against systems you don't own or have explicit written authorization to test.

Tool Landscape

Tool Purpose Speed
Nuclei Template-based vulnerability scanning Fast
Nmap Port/service discovery Fast
SQLMap SQL injection detection Slow
Nikto Web server misconfiguration Medium
OWASP ZAP Full web app scanning Medium
WPScan WordPress-specific scanning Medium
Trivy Container/IaC security Fast
Semgrep SAST (code-level) Fast

Nuclei: Template-Based Scanning

Nuclei is the most practical tool for automated pentest pipelines. It uses YAML templates that define what to check and how to detect it. ProjectDiscovery maintains a library of 6,000+ templates covering CVEs, misconfigurations, and default credentials.

Installation:

go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest

# Or Docker
docker pull projectdiscovery/nuclei:latest

Basic scan:

# Scan with all templates
nuclei -u https://your-app.com -o results.txt

# Scan with specific template categories
nuclei -u https://your-app.com \
  -tags owasp,cve,misconfig \
  -severity critical,high \
  -o results.json -json

Writing a custom template:

# templates/custom-api-key-exposure.yaml
id: api-key-exposure

info:
  name: API Key Exposed in Response
  author: security-team
  severity: high
  tags: custom,secret

requests:
  - method: GET
    path:
      - "{{BaseURL}}/api/config"
      - "{{BaseURL}}/config.json"
      - "{{BaseURL}}/.env"

    matchers-condition: or
    matchers:
      - type: regex
        regex:
          - 'api_key\s*[:=]\s*["\']?[A-Za-z0-9]{20,}'
          - 'secret_key\s*[:=]\s*["\']?[A-Za-z0-9]{20,}'
        condition: or
nuclei -u https://your-app.com -t templates/custom-api-key-exposure.yaml

CI/CD integration:

# .github/workflows/security.yml
- name: Nuclei Security Scan
  run: |
    docker run --rm \
      -v $(pwd):/reports \
      projectdiscovery/nuclei:latest \
        -u https://staging.your-app.com \
        -severity critical,high \
        -tags owasp \
        -o /reports/nuclei-results.json \
        -json \
        -nc  # No color (cleaner CI output)

- name: Check for critical findings
  run: |
    CRITICAL=$(cat nuclei-results.json | jq '[.[] | select(.info.severity == "critical")] | length')
    if [ "$CRITICAL" -gt "0" ]; then
      echo "FAILED: $CRITICAL critical vulnerabilities found"
      cat nuclei-results.json | jq '.[] | select(.info.severity == "critical") | {name: .info.name, url: .matched-at}'
      exit 1
    fi

Nmap: Service Discovery

Nmap maps what ports and services are exposed. Run it to verify your attack surface matches expectations.

# Basic scan
nmap -sV -sC --open 192.168.1.100

# Full port scan
nmap -p- -sV 192.168.1.100

# Script scan for common web vulnerabilities
nmap -p 80,443 --script=http-security-headers,http-methods,http-robots.txt your-app.com

Automated expected-vs-actual check:

#!/bin/bash
# Check that only expected ports are open
EXPECTED="22 80 443"
ACTUAL=$(nmap -p- --open -oG - "$TARGET" | grep "Ports:" | grep -oP '\d+/open' | cut -d/ -f1 | sort -n | tr '\n' ' ')

if [ "$EXPECTED" != "$ACTUAL" ]; then
  echo "ALERT: Unexpected open ports: $ACTUAL (expected: $EXPECTED)"
  exit 1
fi

SQLMap: SQL Injection Testing

SQLMap automates SQL injection detection and exploitation. Use only against systems you own.

# Test a URL parameter
sqlmap -u "https://your-app.com/products?id=1" --batch

# Test a POST form
sqlmap -u "https://your-app.com/login" \
  --data="username=test&password=test" \
  --batch

# Test with cookies (authenticated)
sqlmap -u "https://your-app.com/api/user" \
  --cookie="session=your-session-token" \
  --batch

CI/CD integration (test SQL injection on staging):

# Only test specific parameters, not full exploitation
sqlmap \
  -u "http://staging.your-app.com/search?q=test" \
  --level=2 \     # Test depth (1-5, higher = more aggressive)
  --risk=1 \      # Payload risk (1-3, keep at 1 for safe testing)
  --batch \       # Non-interactive
  --no-logging \  # Reduce noise
  --forms         # Auto-detect and test HTML forms

Nikto: Web Server Scanning

Nikto scans for web server misconfigurations, outdated software, and default files:

nikto -h https://your-app.com -o nikto-report.html -Format html

What Nikto finds:

  • Dangerous HTTP methods (PUT, DELETE enabled)
  • Default files (phpinfo.php, wp-login.php, .git/config)
  • Missing security headers
  • Outdated server software versions
  • Directory listings
  • Common backdoors and shells
# Scan with authentication
nikto -h https://your-app.com \
  -id "admin:password" \  # Basic auth
  -Cgidirs all            # Scan CGI directories

Building a Pentest Pipeline

Architecture:

Code Push
    ↓
Unit + Integration Tests
    ↓
SAST (Semgrep, CodeQL) ← static code analysis
    ↓
Build + Deploy to Staging
    ↓
DAST (ZAP, Nuclei) ← dynamic app testing
    ↓
Infrastructure Scan (Trivy, Nmap) ← environment checks
    ↓
Security Gate (fail if critical findings)
    ↓
Deploy to Production

Full pipeline (GitHub Actions):

name: Security Pipeline

on:
  push:
    branches: [main]

jobs:
  sast:
    name: Static Analysis
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Semgrep
        uses: returntocorp/semgrep-action@v1
        with:
          config: p/owasp-top-ten

  dependency-scan:
    name: Dependency Vulnerabilities
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm audit --audit-level=high
      - name: Trivy filesystem scan
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: fs
          severity: CRITICAL,HIGH
          exit-code: 1

  dast:
    name: Dynamic Security Testing
    runs-on: ubuntu-latest
    needs: [sast]
    steps:
      - name: Deploy to staging
        run: kubectl apply -f k8s/staging/

      - name: Wait for deployment
        run: |
          kubectl rollout status deployment/my-app -n staging --timeout=120s
          /usr/local/bin/await 'curl -sf http://staging.my-app.com/health'

      - name: Nuclei scan
        run: |
          docker run --rm projectdiscovery/nuclei:latest \
            -u http://staging.my-app.com \
            -severity critical,high \
            -json -o /tmp/nuclei.json

      - name: ZAP baseline scan
        uses: zaproxy/action-baseline@v0.11.0
        with:
          target: 'http://staging.my-app.com'
          fail_action: true

  security-gate:
    name: Security Gate
    runs-on: ubuntu-latest
    needs: [sast, dast, dependency-scan]
    steps:
      - name: Check all security jobs passed
        run: echo "All security checks passed"

Scope Management

Automated pentest tools can cause unintended damage if misdirected. Scope management prevents this.

Define allowed targets:

# pentest-scope.yaml
targets:
  - host: staging.your-app.com
    ports: [80, 443, 8080]
    paths:
      - /api/*
      - /public/*
    excluded:
      - /api/admin/*  # Don't test admin endpoints in automation

  - host: 10.0.0.0/24  # Internal network range
    ports: [22, 80, 443, 5432, 6379]
    
exclude_always:
  - production.your-app.com  # Never scan production automatically
  - third-party-service.com  # External services we don't own

Validate scope before running:

#!/bin/bash
TARGET=$1
ALLOWED_TARGETS=("staging.your-app.com" "10.0.0.0/24")

is_in_scope() {
    for allowed in "${ALLOWED_TARGETS[@]}"; do
        if [[ "$TARGET" == *"$allowed"* ]]; then
            return 0
        fi
    done
    return 1
}

if ! is_in_scope "$TARGET"; then
    echo "ERROR: $TARGET is not in scope. Aborting."
    exit 1
fi

echo "Target $TARGET is in scope. Proceeding with scan."

Rate Limiting and Being a Good Scanner

Automated scanners generate significant traffic. Protect your staging environment:

# Nuclei rate limiting
nuclei -u https://staging.your-app.com \
  -rate-limit 50 \     # 50 requests per second
  -bulk-size 10 \      # 10 concurrent requests
  -timeout 10          # 10 second timeout per request

# SQLMap rate limiting
sqlmap -u "http://staging.your-app.com/search?q=1" \
  --delay=1 \          # 1 second between requests
  --max-threads=5      # Max 5 concurrent connections

Interpreting Results

False positive rates by tool (approximate):

Tool False Positive Rate Notes
Nuclei (critical templates) < 5% Well-tested templates
ZAP (SQL Injection) ~20% Needs manual verification
ZAP (XSS) ~30% Many false positives for reflections
Nikto ~40% High noise, focus on high-severity only
SQLMap < 10% More targeted, but slow

For CI/CD integration:

  • Block on: Nuclei critical/high findings, ZAP SQL injection
  • Warn on: ZAP medium findings, Nikto
  • Review separately: Low severity, informational

Always manually verify critical findings before treating them as confirmed vulnerabilities. A scanner reporting "SQL Injection found" requires a human to confirm it's exploitable, understand the impact, and prioritize the fix.

Read more

Start now free