1. Automated Reporting Pipeline
Automated Security Reporting Pipeline:
Security Tools ──▶ Normalize ──▶ Aggregate ──▶ Report
├── Nuclei ├── SARIF ├── DefectDojo ├── PDF/DOCX
├── ZAP ├── JSON ├── Jira ├── Dashboard
├── Trivy ├── CSV ├── GitHub ├── Slack/Email
├── SonarQube └── XML └── ServiceNow └── Metrics
└── k6 (perf)
Integration Points:
├── CI/CD: Generate reports on each pipeline run
├── Ticketing: Auto-create issues for findings
├── Dashboard: Real-time vulnerability metrics
└── Notifications: Alert on critical findings
2. DefectDojo — Vulnerability Management
# DefectDojo — Open-source vulnerability management
# Install with Docker
git clone https://github.com/DefectDojo/django-DefectDojo.git
cd django-DefectDojo
docker compose up -d
# Access: http://localhost:8080
# Default credentials in docker compose logs
# --- Import scan results via API ---
# Create product
curl -X POST "http://localhost:8080/api/v2/products/" \
-H "Authorization: Token YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Example App",
"description": "Main web application",
"prod_type": 1
}'
# Import Nuclei scan
curl -X POST "http://localhost:8080/api/v2/import-scan/" \
-H "Authorization: Token YOUR_API_TOKEN" \
-F "file=@nuclei_results.json" \
-F "scan_type=Nuclei Scan" \
-F "product_name=Example App" \
-F "engagement_name=Q2 2026 Pentest" \
-F "auto_create_context=true" \
-F "deduplication_on_engagement=true"
# Import ZAP scan
curl -X POST "http://localhost:8080/api/v2/import-scan/" \
-H "Authorization: Token YOUR_API_TOKEN" \
-F "file=@zap_report.xml" \
-F "scan_type=ZAP Scan" \
-F "product_name=Example App" \
-F "engagement_name=Q2 2026 Pentest"
# Import Trivy scan
curl -X POST "http://localhost:8080/api/v2/import-scan/" \
-H "Authorization: Token YOUR_API_TOKEN" \
-F "file=@trivy_results.json" \
-F "scan_type=Trivy Scan" \
-F "product_name=Example App"
# Get findings summary
curl "http://localhost:8080/api/v2/findings/?product=1&active=true" \
-H "Authorization: Token YOUR_API_TOKEN" | jq '.count'
3. CI/CD Reporting Integration
# .github/workflows/security-report.yml
name: Security Scan & Report
on:
push:
branches: [main]
schedule:
- cron: '0 6 * * 1' # Every Monday 6AM
jobs:
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# SAST — SonarQube
- name: SonarQube Scan
uses: sonarsource/sonarqube-scan-action@v3
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
# Container scanning — Trivy
- name: Build and scan image
run: |
docker build -t myapp:${{ github.sha }} .
trivy image --format json --output trivy.json \
--severity HIGH,CRITICAL myapp:${{ github.sha }}
# DAST — ZAP baseline
- name: Start app
run: docker compose up -d && sleep 30
- name: ZAP Scan
uses: zaproxy/[email protected]
with:
target: 'http://localhost:3000'
# Dependency audit
- name: npm audit
run: npm audit --json > npm-audit.json || true
# Upload all results to DefectDojo
- name: Upload to DefectDojo
run: |
# Trivy results
curl -X POST "${{ secrets.DEFECTDOJO_URL }}/api/v2/reimport-scan/" \
-H "Authorization: Token ${{ secrets.DEFECTDOJO_TOKEN }}" \
-F "[email protected]" \
-F "scan_type=Trivy Scan" \
-F "product_name=Example App" \
-F "engagement_name=CI/CD Scans" \
-F "auto_create_context=true" \
-F "deduplication_on_engagement=true"
# ZAP results
curl -X POST "${{ secrets.DEFECTDOJO_URL }}/api/v2/reimport-scan/" \
-H "Authorization: Token ${{ secrets.DEFECTDOJO_TOKEN }}" \
-F "file=@report_html.html" \
-F "scan_type=ZAP Scan" \
-F "product_name=Example App"
# Performance test
- name: k6 Load Test
run: |
k6 run --out json=k6-results.json tests/load-test.js
# Generate combined report
- name: Generate Report
run: python scripts/generate-report.py
# Notify on critical findings
- name: Slack notification
if: failure()
uses: 8398a7/action-slack@v3
with:
status: failure
text: '🚨 Critical security findings detected!'
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
4. Jira/GitHub Issues Automation
# auto_create_issues.py — Auto-create Jira tickets from findings
import requests
import json
class SecurityIssueCreator:
def __init__(self, jira_url: str, jira_token: str, project_key: str):
self.jira_url = jira_url
self.headers = {
"Authorization": f"Basic {jira_token}",
"Content-Type": "application/json"
}
self.project_key = project_key
def create_issue_from_finding(self, finding: dict) -> str:
"""Create Jira ticket from security finding"""
severity_map = {
"Critical": "Highest",
"High": "High",
"Medium": "Medium",
"Low": "Low"
}
sla_map = {
"Critical": "48 hours",
"High": "1 week",
"Medium": "2 weeks",
"Low": "1 month"
}
issue_data = {
"fields": {
"project": {"key": self.project_key},
"summary": f"[Security] {finding['title']}",
"description": self._format_description(finding),
"issuetype": {"name": "Bug"},
"priority": {"name": severity_map.get(finding["severity"], "Medium")},
"labels": ["security", "pentest", f"cvss-{finding['cvss']}"],
"customfield_10100": sla_map.get(finding["severity"]), # SLA field
}
}
response = requests.post(
f"{self.jira_url}/rest/api/3/issue",
headers=self.headers,
json=issue_data
)
response.raise_for_status()
issue_key = response.json()["key"]
return issue_key
def _format_description(self, finding: dict) -> str:
return f"""
h2. Security Finding: {finding['title']}
||Attribute||Value||
|Severity|{finding['severity']}|
|CVSS v4.0|{finding['cvss']}|
|CWE|{finding.get('cwe', 'N/A')}|
|OWASP|{finding.get('owasp', 'N/A')}|
|Affected|{finding['affected']}|
h3. Description
{finding['description']}
h3. Steps to Reproduce
{finding['steps']}
h3. Business Impact
{finding['impact']}
h3. Remediation
{finding['remediation']}
h3. References
{finding.get('references', 'N/A')}
"""
# Usage
creator = SecurityIssueCreator(
jira_url="https://company.atlassian.net",
jira_token="base64_encoded_email:token",
project_key="SEC"
)
# From DefectDojo findings
findings = requests.get(
"http://defectdojo:8080/api/v2/findings/?active=true&severity=Critical",
headers={"Authorization": "Token dojo_token"}
).json()["results"]
for finding in findings:
issue_key = creator.create_issue_from_finding({
"title": finding["title"],
"severity": finding["severity"],
"cvss": finding["cvssv3_score"],
"description": finding["description"],
"affected": finding["file_path"],
"steps": finding["steps_to_reproduce"],
"impact": finding["impact"],
"remediation": finding["mitigation"],
"cwe": finding["cwe"],
})
print(f"Created {issue_key}: {finding['title']}")
5. Remediation SLA Tracking
Vulnerability SLA Framework:
Severity │ Detection → Triage │ Triage → Fix │ Fix → Verify
───────────┼────────────────────┼───────────────┼─────────────
Critical │ 4 hours │ 48 hours │ 24 hours
High │ 8 hours │ 7 days │ 3 days
Medium │ 24 hours │ 30 days │ 7 days
Low │ 48 hours │ 90 days │ 14 days
SLA Compliance Dashboard:
On-time │ ████████████████████████ 78%
At-risk │ █████████ 15%
Overdue │ ████ 7%
Overdue Breakdown:
├── Critical: 0 (target: always 0)
├── High: 2 findings (avg 3 days overdue)
├── Medium: 5 findings (avg 10 days overdue)
└── Low: 8 findings (avg 20 days overdue)
Escalation Process:
Day 0: Finding created → assigned to team
SLA - 2d: Warning notification to team lead
SLA: Escalation to engineering manager
SLA + 2d: Escalation to VP Engineering
SLA + 7d: Escalation to CISO
# Grafana dashboard for vulnerability metrics
# datasource: DefectDojo PostgreSQL
# Panel 1: Findings by Severity (Pie chart)
# SELECT severity, COUNT(*) FROM dojo_finding
# WHERE active = true GROUP BY severity
# Panel 2: SLA Compliance (Stat)
# SELECT
# COUNT(CASE WHEN resolved_at <= sla_deadline THEN 1 END) * 100.0 / COUNT(*)
# FROM dojo_finding WHERE resolved_at IS NOT NULL
# Panel 3: Mean Time to Remediate (Time series)
# SELECT date_trunc('week', resolved_at),
# AVG(resolved_at - created_at)
# FROM dojo_finding
# WHERE resolved_at IS NOT NULL
# GROUP BY 1 ORDER BY 1
# Panel 4: Open Findings Trend (Time series)
# Uses DefectDojo API: /api/v2/finding_count_by_date/
6. Vulnerability Lifecycle Management
Vulnerability Lifecycle:
Discovery → Triage → Assign → Fix → Verify → Close
│ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼
Scanner/ Confirm/ Dev Deploy Retest Update
Pentest Dedup team fix by report
pentester
States:
├── New: Just discovered, not yet reviewed
├── Confirmed: Validated as true positive
├── Assigned: Assigned to dev team
├── In Progress: Fix being developed
├── Fixed: Code fix deployed
├── Verified: Retested and confirmed fixed
├── Closed: Finding resolved
├── Risk Accepted: Business accepts the risk
├── False Positive: Confirmed false positive
└── Duplicate: Same as existing finding
Metrics to Track:
├── MTTD (Mean Time to Detect)
├── MTTR (Mean Time to Remediate)
├── SLA Compliance %
├── Reopen Rate (fixed but came back)
├── False Positive Rate
├── Risk Acceptance Rate
└── Finding Trend (new vs closed per week)
7. Summary
- Automated Pipeline: Scanner → Normalize → DefectDojo → Ticketing
- DefectDojo: Central vulnerability management — import, dedup, track
- CI/CD Integration: Auto-scan on every push, report to DefectDojo
- Ticketing: Auto-create Jira/GitHub issues from critical findings
- SLA Tracking: Define and enforce remediation timelines per severity
- Metrics: MTTD, MTTR, SLA compliance, trend dashboards
The next (final!) post will be Capstone Project — End-to-end.