
Defectdojo
- 92 installs
- 6 repo stars
- Updated July 22, 2026
- julianobarbosa/claude-code-skills
Helps with ai & agent building tasks.
About
defectdojo is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- defectdojo
- AI & Agent Building
- AI-coding skill
Defectdojo by the numbers
- 92 all-time installs (skills.sh)
- +1 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #4,749 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/julianobarbosa/claude-code-skills --skill defectdojoAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 92 |
|---|---|
| repo stars | ★ 6 |
| Last updated | July 22, 2026 |
| Repository | julianobarbosa/claude-code-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
DefectDojo Skill
Overview
DefectDojo is an open-source DevSecOps, Application Security Posture Management (ASPM), and vulnerability management platform. It orchestrates end-to-end security testing, vulnerability tracking, deduplication, remediation, and reporting.
Key Capabilities:
- Unified vulnerability management across 200+ security tools
- Automated scan import and deduplication
- CI/CD pipeline integration
- Bidirectional JIRA integration
- Role-based access control
- SLA tracking and reporting
- REST API v2 for automation
- MCP Tools for Claude Code integration
Official Resources:
- Documentation: <https://docs.defectdojo.com/>
- GitHub: <https://github.com/DefectDojo/django-DefectDojo>
- Demo: <https://demo.defectdojo.org> (admin / 1Defectdojo@demo#appsec)
MCP Tools (Primary Interface)
This skill provides 12 MCP tools for direct DefectDojo API interaction. Use these tools instead of manual API calls.
Read Operations
| Tool | Description | Key Parameters |
|---|---|---|
defectdojo_list_products | List and search products | name_contains, prod_type, limit |
defectdojo_get_product | Get detailed product info | product_id (required) |
defectdojo_list_engagements | List engagements with filters | product_id, status, engagement_type |
defectdojo_list_tests | List tests in engagements | engagement_id, test_type |
defectdojo_list_findings | Primary tool - Search findings | severity, active, product_id, cwe |
defectdojo_get_finding | Get finding details | finding_id (required) |
defectdojo_get_statistics | Vulnerability statistics | product_id, engagement_id |
defectdojo_list_endpoints | List product endpoints | product_id, host, protocol |
defectdojo_list_test_types | List scanner types | name_contains |
Write Operations
| Tool | Description | Key Parameters |
|---|---|---|
defectdojo_create_engagement | Create new engagement | product_id, name, engagement_type |
defectdojo_update_finding | Update finding status | finding_id, active, verified, false_p |
defectdojo_close_engagement | Close engagement | engagement_id |
Usage Examples
List all critical active findings:
Use defectdojo_list_findings with:
- severity: "Critical"
- active: trueGet vulnerability statistics for a product:
Use defectdojo_get_statistics with:
- product_id: 1Search for SQL injection findings:
Use defectdojo_list_findings with:
- cwe: 89
- active: trueMark a finding as false positive:
Use defectdojo_update_finding with:
- finding_id: 123
- false_p: true
- active: falseCreate a CI/CD engagement:
Use defectdojo_create_engagement with:
- product_id: 1
- name: "Pipeline Security Scan"
- engagement_type: "CI/CD"Response Formats
All tools support two output formats via the response_format parameter:
markdown(default) - Human-readable formatted outputjson- Raw JSON for programmatic processing
MCP Server Configuration
The MCP server is configured in .mcp.json:
{
"mcpServers": {
"defectdojo": {
"command": "python",
"args": [".claude/mcp-servers/defectdojo-mcp/defectdojo_mcp.py"],
"env": {
"DEFECTDOJO_URL": "https://defectdojo.dev.cafehyna.com.br",
"DEFECTDOJO_API_TOKEN": "${DEFECTDOJO_API_TOKEN}"
}
}
}
}Environment Variables:
DEFECTDOJO_URL- Your DefectDojo instance URLDEFECTDOJO_API_TOKEN- API token from/api/key-v2
Data Model (Product Hierarchy)
DefectDojo uses five interconnected data classes to organize security work:
Product Type
└── Product
└── Engagement (CI/CD or Interactive)
└── Test
└── Finding
└── EndpointProduct Types
The topmost organizational level that categorizes products by business domain, team, or security area. Enables role-based access control at the category level.
Products
Individual applications or systems under security testing. Each product maintains:
- Its own testing history
- Deduplication scope (findings deduplicate within products)
- SLA configuration
- Team assignments
Engagements
Scheduled testing periods containing one or more tests. Two types:
| Type | Purpose | Use Case |
|---|---|---|
| CI/CD | Automated pipeline integration | Automated scans per build/commit |
| Interactive | Manual testing by engineers | Penetration tests, manual reviews |
Tests
Individual security scans grouped by tool type. Tests support:
- Reimporting (add findings to existing test)
- Environment tagging
- Version tracking
Findings
Specific vulnerabilities discovered during testing:
| Severity | Description |
|---|---|
| Critical | Immediate action required |
| High | High priority remediation |
| Medium | Standard priority |
| Low | Low priority |
| Info | Informational only |
Finding States:
- Active / Inactive
- Verified / Unverified
- Duplicate
- Mitigated
- False Positive
- Risk Accepted
- Out of Scope
Endpoints
References to affected hosts, URLs, or systems. Enables vulnerability tracking by infrastructure component.
API v2 Reference
Note: For most operations, use the MCP Tools above instead of direct API calls. Use direct API calls only for scan imports or operations not covered by MCP tools.
Authentication
Generate API token at: <your-instance>/api/key-v2
# Header format
Authorization: Token <api_key>Environment Variables:
DD_API_TOKENS_ENABLED=False- Disable API tokens entirelyDD_API_TOKEN_AUTH_ENDPOINT_ENABLED=False- Disable only token auth endpoint
Core Endpoints
| Endpoint | Method | Purpose |
|---|---|---|
/api/v2/import-scan/ | POST | Initial scan import |
/api/v2/reimport-scan/ | POST | Subsequent imports (deduplication) |
/api/v2/products/ | GET/POST | Manage products |
/api/v2/engagements/ | GET/POST | Manage engagements |
/api/v2/tests/ | GET/POST | Manage tests |
/api/v2/findings/ | GET/POST/PATCH | Manage findings |
/api/v2/endpoints/ | GET/POST | Manage endpoints |
/api/v2/users/ | GET | List users |
Import Scan Parameters
curl -X POST "https://defectdojo.example.com/api/v2/import-scan/" \
-H "Authorization: Token <api-token>" \
-F "scan_type=<scanner-type>" \
-F "file=@results.json" \
-F "engagement=<engagement-id>" \
-F "minimum_severity=Info" \
-F "active=true" \
-F "verified=false" \
-F "scan_date=2024-01-15"Key Parameters:
| Parameter | Description |
|---|---|
scan_type | Scanner identifier (e.g., "Trivy Scan", "Semgrep JSON Report") |
engagement | Target engagement ID |
test_title | Custom test name |
minimum_severity | Filter threshold (Info, Low, Medium, High, Critical) |
active | Mark findings as active (boolean) |
verified | Mark findings as verified (boolean) |
scan_date | Override scan completion date |
do_not_reactivate | Prevent reopening closed findings |
auto_create_context | Auto-create Product/Engagement if missing |
Reimport Scan (Deduplication)
curl -X POST "https://defectdojo.example.com/api/v2/reimport-scan/" \
-H "Authorization: Token <api-token>" \
-F "scan_type=Trivy Scan" \
-F "file=@trivy-results.json" \
-F "test=<test-id>" \
-F "do_not_reactivate=true"The reimport endpoint:
- Detects new vs. existing findings
- Updates existing findings
- Closes findings not in the new scan
- Can auto-create context when
auto_create_context=true
Interactive API Documentation
Access Swagger UI at: <your-instance>/api/v2/oa3/swagger-ui/
CI/CD Integration
Pipeline Integration Pattern
# GitLab CI Example
stages:
- security-scan
- upload-results
trivy-scan:
stage: security-scan
script:
- trivy image --format json -o trivy-results.json $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
artifacts:
paths:
- trivy-results.json
upload-to-defectdojo:
stage: upload-results
script: |
curl -X POST "${DEFECTDOJO_URL}/api/v2/reimport-scan/" \
-H "Authorization: Token ${DEFECTDOJO_API_TOKEN}" \
-F "scan_type=Trivy Scan" \
-F "file=@trivy-results.json" \
-F "product_name=${CI_PROJECT_NAME}" \
-F "engagement_name=CI/CD-${CI_PIPELINE_ID}" \
-F "auto_create_context=true" \
-F "minimum_severity=Low"Jenkins Integration
Install the DefectDojo Jenkins plugin from: <https://plugins.jenkins.io/defectdojo/>
Pipeline Configuration:
pipeline {
agent any
environment {
DEFECTDOJO_URL = 'https://defectdojo.example.com'
DEFECTDOJO_API_KEY = credentials('defectdojo-api-key')
}
stages {
stage('Security Scan') {
steps {
sh 'trivy image --format json -o trivy.json myapp:latest'
}
}
stage('Upload to DefectDojo') {
steps {
defectDojoPublisher(
artifact: 'trivy.json',
productName: 'MyApp',
scanType: 'Trivy Scan',
engagementName: "Build-${BUILD_NUMBER}"
)
}
}
}
}GitHub Actions Integration
name: Security Scan
on: [push]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Trivy
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
format: 'json'
output: 'trivy-results.json'
- name: Upload to DefectDojo
run: |
curl -X POST "${{ secrets.DEFECTDOJO_URL }}/api/v2/reimport-scan/" \
-H "Authorization: Token ${{ secrets.DEFECTDOJO_TOKEN }}" \
-F "scan_type=Trivy Scan" \
-F "file=@trivy-results.json" \
-F "product_name=${{ github.repository }}" \
-F "engagement_name=GitHub-${{ github.run_id }}" \
-F "auto_create_context=true"Python API Examples
Tip: For Claude Code interactions, use the MCP tools (defectdojo_list_findings, etc.) instead of writing Python code. The examples below are for CI/CD scripts and external integrations.Basic API Connection
import requests
class DefectDojoAPI:
def __init__(self, url, api_token):
self.url = url.rstrip('/')
self.headers = {
'Authorization': f'Token {api_token}',
'Accept': 'application/json'
}
def get_products(self):
response = requests.get(
f'{self.url}/api/v2/products/',
headers=self.headers
)
response.raise_for_status()
return response.json()
def import_scan(self, engagement_id, scan_type, file_path, **kwargs):
with open(file_path, 'rb') as f:
data = {
'engagement': engagement_id,
'scan_type': scan_type,
'minimum_severity': kwargs.get('minimum_severity', 'Info'),
'active': kwargs.get('active', True),
'verified': kwargs.get('verified', False),
}
files = {'file': f}
response = requests.post(
f'{self.url}/api/v2/import-scan/',
headers={'Authorization': self.headers['Authorization']},
data=data,
files=files
)
response.raise_for_status()
return response.json()
# Usage
api = DefectDojoAPI('https://defectdojo.example.com', 'your-api-token')
products = api.get_products()Create Product and Engagement
def create_product(api, name, prod_type_id, description=''):
response = requests.post(
f'{api.url}/api/v2/products/',
headers=api.headers,
json={
'name': name,
'prod_type': prod_type_id,
'description': description
}
)
response.raise_for_status()
return response.json()
def create_engagement(api, product_id, name, target_start, target_end,
engagement_type='CI/CD'):
response = requests.post(
f'{api.url}/api/v2/engagements/',
headers=api.headers,
json={
'name': name,
'product': product_id,
'target_start': target_start,
'target_end': target_end,
'engagement_type': engagement_type,
'status': 'In Progress'
}
)
response.raise_for_status()
return response.json()Query Findings
def get_findings(api, product_id=None, severity=None, active=True):
params = {'active': active}
if product_id:
params['test__engagement__product'] = product_id
if severity:
params['severity'] = severity
response = requests.get(
f'{api.url}/api/v2/findings/',
headers=api.headers,
params=params
)
response.raise_for_status()
return response.json()
# Get all critical findings
critical = get_findings(api, severity='Critical')Supported Security Tools (200+)
SAST / Code Analysis
- Bandit, Checkmarx, Fortify, SonarQube, Semgrep
- CodeQL, Horusec, Brakeman, SpotBugs
Dependency / SCA
- Snyk, OWASP Dependency-Check, Dependency-Track
- npm Audit, pip-audit, Trivy, Safety
DAST / Web Scanning
- Burp Suite, OWASP ZAP, Nikto, Nessus
- Qualys, OpenVAS, Acunetix, AppScan
Container / Infrastructure
- Trivy, Aqua, Anchore, Wiz, NeuVector
- kube-bench, Kubescape, Prisma Cloud
Secrets Detection
- Gitleaks, Trufflehog, Detect-secrets
- GitHub Secret Scanning
Cloud Security
- AWS Inspector, AWS Prowler, ScoutSuite
- Azure Security Center, Checkov
IaC Scanning
- Checkov, Terrascan, KICS, TFSec, Dockle
Full list: <https://docs.defectdojo.com/supported_tools/>
JIRA Integration
Configuration
1. Enable in System Settings:
Configuration > System Settings > Enable JIRA Integration2. Add JIRA Instance:
Enterprise Settings > JIRA Instances > + New JIRA Instance3. Configure Webhook (bidirectional sync):
- Create webhook in JIRA pointing to:
https://<defectdojo>/jira/webhook/<webhook-secret>
- Enable in DefectDojo: "Enable JIRA web hook"
Environment Variables
extraEnv:
- name: DD_JIRA_URL
value: "https://your-jira.atlassian.net"
- name: DD_JIRA_MAX_RETRIES
value: "3"Features
- Push findings to JIRA as issues
- Bidirectional comment sync
- Auto-close findings when JIRA issues close
- SLA notifications as JIRA comments
Project File Locations
| File Type | Path |
|---|---|
| ApplicationSet | infra-team/applicationset/defectdojo.yaml |
| Helm Values | argo-cd-helm-values/kube-addons/defectdojo/<cluster>/values.yaml |
| SecretProviderClass | argo-cd-helm-values/kube-addons/defectdojo/<cluster>/secretproviderclass.yaml |
Environment Configuration
| Cluster | Key Vault | Azure AD Tenant ID |
|---|---|---|
| cafehyna-dev | kv-cafehyna-dev-hlg | 3f7a3df4-f85b-4ca8-98d0-08b1034e6567 |
Azure AD App Registration
| Setting | Value |
|---|---|
| Application (Client) ID | 79ada8c7-4270-41e8-9ea0-1e1e62afff3d |
| Tenant ID | 3f7a3df4-f85b-4ca8-98d0-08b1034e6567 |
| Redirect URI | https://defectdojo.dev.cafehyna.com.br/complete/azuread-tenant-oauth2/ |
Azure AD SSO Configuration
Required Environment Variables
extraEnv:
# Enable Azure AD OAuth2
- name: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_ENABLED
value: "True"
# Application (Client) ID
- name: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_KEY
value: "<client-id>"
# Directory (Tenant) ID
- name: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_TENANT_ID
value: "<tenant-id>"
# Client Secret (from Key Vault)
- name: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_SECRET
valueFrom:
secretKeyRef:
name: defectdojo
key: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_SECRETGroup Synchronization
extraEnv:
# Sync groups from Azure AD token
- name: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_GET_GROUPS
value: "True"
# Remove users from groups when removed in Azure AD
- name: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_CLEANUP_GROUPS
value: "True"
# Filter to only sync DefectDojo groups
- name: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_GROUPS_FILTER
value: "^G-Usuarios-DefectDojo-.*"Required Azure AD Permissions (Application type, Admin consent required):
Group.Read.AllGroupMember.Read.AllUser.Read.All
For complete Azure AD SSO details, see references/azure-ad-sso.md.
DefectDojo Roles
| Role | Permissions |
|---|---|
| Superuser | Full system access, manage users, system settings |
| Owner | Delete products, designate other owners |
| Maintainer | Edit products, add members, delete findings |
| Writer | Add/edit engagements, tests, findings |
| Reader | View-only, add comments |
| API Importer | Limited API access for CI/CD pipelines |
Azure AD Groups for Role Mapping
| Azure AD Group | DefectDojo Role |
|---|---|
G-Usuarios-DefectDojo-Superuser | Superuser |
G-Usuarios-DefectDojo-Owner | Owner |
G-Usuarios-DefectDojo-Maintainer | Maintainer |
G-Usuarios-DefectDojo-Writer | Writer |
G-Usuarios-DefectDojo-Reader | Reader |
Helm Chart Quick Reference
Key Values
# Host configuration
host: defectdojo.dev.cafehyna.com.br
siteUrl: https://defectdojo.dev.cafehyna.com.br
# Secrets (use CSI driver)
createSecret: false
disableHooks: true
# Django
django:
replicas: 1
ingress:
enabled: true
activateTLS: true
className: nginx
# Celery (keep beat at 1 replica)
celery:
beat:
enabled: true
replicas: 1
worker:
enabled: true
replicas: 1
# Database
postgresql:
enabled: true
# Cache
redis:
enabled: trueFor complete Helm values reference, see references/helm-values.md.
Kubernetes Deployment
Basic Helm Install
git clone https://github.com/DefectDojo/django-DefectDojo
cd django-DefectDojo
helm install defectdojo ./helm/defectdojo \
-n defectdojo --create-namespace \
--set django.ingress.enabled=true \
--set django.ingress.activateTLS=false \
--set createSecret=true \
--set createRabbitMqSecret=true \
--set createPostgresqlSecret=trueAccess DefectDojo
kubectl port-forward --namespace=defectdojo service/defectdojo-django 8080:80Secrets Management
Secrets are managed via Azure Key Vault CSI Driver:
| Key Vault Secret | K8s Secret Key | Purpose |
|---|---|---|
defectdojo-admin-password | DD_ADMIN_PASSWORD | Admin user password |
defectdojo-secret-key | DD_SECRET_KEY | Django secret key |
defectdojo-credential-aes-key | DD_CREDENTIAL_AES_256_KEY | Credential encryption |
defectdojo-azuread-client-secret | DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_SECRET | Azure AD client secret |
Common Troubleshooting
User Not in Groups After SSO Login
Symptoms: User logged in via Azure AD but shows "No group members found"
Solutions:
1. Verify DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_GET_GROUPS=True 2. Check Azure AD API permissions (Group.Read.All with admin consent) 3. Verify Azure AD token includes group claim (not role claims) 4. User must log out and log back in to sync groups 5. Create matching groups in DefectDojo UI
HTTPS Redirect URI Mismatch (ADSTS50011)
Error: "The redirect URI specified in the request does not match"
Solution: Ensure these are set:
- name: DD_SESSION_COOKIE_SECURE
value: "True"
- name: DD_CSRF_COOKIE_SECURE
value: "True"
- name: DD_SECURE_PROXY_SSL_HEADER
value: "True"ERR_TOO_MANY_REDIRECTS
Cause: DD_SECURE_SSL_REDIRECT=True with TLS-terminating proxy
Solution: Set DD_SECURE_SSL_REDIRECT=False when behind NGINX Ingress
Emergency Login Access
If SSO breaks, access standard login form:
https://defectdojo.dev.cafehyna.com.br/login?force_login_formFor complete troubleshooting guide, see references/troubleshooting.md.
Useful Commands
Check Pod Status
KUBECONFIG=~/.kube/aks-rg-hypera-cafehyna-dev-config kubectl get pods -n defectdojoView Logs
KUBECONFIG=~/.kube/aks-rg-hypera-cafehyna-dev-config kubectl logs -n defectdojo -l app.kubernetes.io/name=defectdojo -c uwsgiRestart Deployment
KUBECONFIG=~/.kube/aks-rg-hypera-cafehyna-dev-config kubectl rollout restart deployment/defectdojo-django -n defectdojoAdditional References
MCP Server
- MCP Server README - MCP server setup and usage
- MCP Server Source - Server implementation
Skill References
- Azure AD SSO Configuration - Complete SSO setup guide
- Helm Values Reference - Full Helm chart configuration
- Troubleshooting Guide - Common issues and solutions
- API v2 Reference - Complete API documentation
- CI/CD Integration Guide - Pipeline integration patterns
External
- Official Documentation
- Swagger UI - Interactive API docs
---
Gotchas
- Deduplication is per-product, not global: The same CVE in two products counts twice — portfolio metrics inflate. Configure cross-product deduplication via hash_code algorithm if you need global counts.
- `reimport-scan` closes missing findings silently: Reimporting a partial scan (one path instead of full) auto-closes every finding not in the new file. Use
do_not_reactivate=trueand scope tests carefully. - `auto_create_context=true` creates duplicate products on name drift: "MyApp" vs "myapp" vs "MyApp " produce three products. Normalize
product_nameupstream — DefectDojo does not fuzzy-match. - API tokens are user-scoped, not team-scoped: A pipeline token inherits the creator's full permissions. Create a dedicated CI user with API Importer role rather than reusing a human's token.
- `DD_SECURE_SSL_REDIRECT=True` behind NGINX Ingress causes redirect loops: TLS terminates at the ingress, Django then redirects HTTP to HTTPS again. Set to
Falseand rely on the ingress for TLS enforcement. - Azure AD group sync only fires on login: Adding a user to an Azure AD group does not retro-sync — the user must log out and back in before DefectDojo sees the new membership.
DefectDojo API v2 Complete Reference
Authentication
API Token Generation
Generate your API token at: <your-instance>/api/key-v2
Request Headers
Authorization: Token <your-api-token>
Content-Type: application/json # For JSON requests
Accept: application/jsonPython Example
import requests
headers = {
'Authorization': 'Token c8572a5adf107a693aa6c72584da31f4d1f1dcff',
'Accept': 'application/json'
}
response = requests.get(
'https://defectdojo.example.com/api/v2/products/',
headers=headers
)Environment Variables
| Variable | Description |
|---|---|
DD_API_TOKENS_ENABLED | Set to False to disable all API tokens |
DD_API_TOKEN_AUTH_ENDPOINT_ENABLED | Set to False to disable only /api/v2/api-token-auth/ |
Products API
List Products
curl -X GET "https://defectdojo.example.com/api/v2/products/" \
-H "Authorization: Token <api-token>"Query Parameters:
name- Filter by exact namename__contains- Filter by partial name matchprod_type- Filter by product type IDoffset- Pagination offsetlimit- Number of results per page
Create Product
curl -X POST "https://defectdojo.example.com/api/v2/products/" \
-H "Authorization: Token <api-token>" \
-H "Content-Type: application/json" \
-d '{
"name": "My Application",
"description": "Description of the application",
"prod_type": 1
}'Required Fields:
name(string)prod_type(integer) - Product type ID
Optional Fields:
description(string)tags(array of strings)business_criticality(string): "very high", "high", "medium", "low", "very low", "none"platform(string): "web service", "desktop", "iot", "mobile", "web"lifecycle(string): "construction", "production", "retirement"origin(string): "third party library", "purchased", "contractor", "internal", "open source", "outsourced"external_audience(boolean)internet_accessible(boolean)
Get Product by ID
curl -X GET "https://defectdojo.example.com/api/v2/products/1/" \
-H "Authorization: Token <api-token>"Update Product
curl -X PATCH "https://defectdojo.example.com/api/v2/products/1/" \
-H "Authorization: Token <api-token>" \
-H "Content-Type: application/json" \
-d '{
"description": "Updated description"
}'Product Types API
List Product Types
curl -X GET "https://defectdojo.example.com/api/v2/product_types/" \
-H "Authorization: Token <api-token>"Create Product Type
curl -X POST "https://defectdojo.example.com/api/v2/product_types/" \
-H "Authorization: Token <api-token>" \
-H "Content-Type: application/json" \
-d '{
"name": "Web Applications",
"description": "All web-based applications"
}'Engagements API
List Engagements
curl -X GET "https://defectdojo.example.com/api/v2/engagements/" \
-H "Authorization: Token <api-token>"Query Parameters:
product- Filter by product IDengagement_type- "Interactive" or "CI/CD"status- "Not Started", "In Progress", "Completed", "Cancelled"name__contains- Partial name match
Create Engagement
curl -X POST "https://defectdojo.example.com/api/v2/engagements/" \
-H "Authorization: Token <api-token>" \
-H "Content-Type: application/json" \
-d '{
"name": "Q1 2024 Security Review",
"product": 1,
"target_start": "2024-01-01",
"target_end": "2024-03-31",
"engagement_type": "Interactive",
"status": "In Progress"
}'Required Fields:
name(string)product(integer) - Product IDtarget_start(date) - YYYY-MM-DD formattarget_end(date) - YYYY-MM-DD format
Optional Fields:
engagement_type(string): "Interactive" or "CI/CD"status(string): "Not Started", "In Progress", "Completed", "Cancelled"description(string)lead(integer) - User IDbuild_id(string) - CI/CD build identifiercommit_hash(string)branch_tag(string)source_code_management_uri(string)deduplication_on_engagement(boolean)
Tests API
List Tests
curl -X GET "https://defectdojo.example.com/api/v2/tests/" \
-H "Authorization: Token <api-token>"Query Parameters:
engagement- Filter by engagement IDtest_type- Filter by test type IDtitle__contains- Partial title match
Create Test
curl -X POST "https://defectdojo.example.com/api/v2/tests/" \
-H "Authorization: Token <api-token>" \
-H "Content-Type: application/json" \
-d '{
"engagement": 1,
"test_type": 1,
"target_start": "2024-01-15",
"target_end": "2024-01-15"
}'Findings API
List Findings
curl -X GET "https://defectdojo.example.com/api/v2/findings/" \
-H "Authorization: Token <api-token>"Query Parameters:
test- Filter by test IDtest__engagement- Filter by engagement IDtest__engagement__product- Filter by product IDseverity- "Critical", "High", "Medium", "Low", "Info"active- Booleanverified- Booleanduplicate- Booleanfalse_p- Boolean (false positive)cwe- CWE IDtitle__contains- Partial title match
Get Finding by ID
curl -X GET "https://defectdojo.example.com/api/v2/findings/1/" \
-H "Authorization: Token <api-token>"Update Finding
curl -X PATCH "https://defectdojo.example.com/api/v2/findings/1/" \
-H "Authorization: Token <api-token>" \
-H "Content-Type: application/json" \
-d '{
"active": false,
"verified": true,
"false_p": false
}'Create Finding Manually
curl -X POST "https://defectdojo.example.com/api/v2/findings/" \
-H "Authorization: Token <api-token>" \
-H "Content-Type: application/json" \
-d '{
"title": "SQL Injection in Login",
"description": "Detailed description of the vulnerability",
"severity": "High",
"test": 1,
"active": true,
"verified": true,
"cwe": 89,
"mitigation": "Use parameterized queries"
}'Required Fields:
title(string)severity(string): "Critical", "High", "Medium", "Low", "Info"test(integer) - Test ID
Import Scan API
Import Scan (First Import)
curl -X POST "https://defectdojo.example.com/api/v2/import-scan/" \
-H "Authorization: Token <api-token>" \
-F "scan_type=Trivy Scan" \
-F "file=@trivy-results.json" \
-F "engagement=1" \
-F "minimum_severity=Info" \
-F "active=true" \
-F "verified=false"Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
scan_type | string | Yes | Scanner identifier (see Supported Parsers) |
file | file | Yes | Scan results file |
engagement | integer | Conditional | Engagement ID (required if not using auto_create) |
product_type_name | string | No | For auto_create_context |
product_name | string | No | For auto_create_context |
engagement_name | string | No | For auto_create_context |
test_title | string | No | Custom test name |
minimum_severity | string | No | "Info", "Low", "Medium", "High", "Critical" |
active | boolean | No | Mark findings as active |
verified | boolean | No | Mark findings as verified |
scan_date | date | No | Override scan completion date |
auto_create_context | boolean | No | Auto-create Product/Engagement |
close_old_findings | boolean | No | Close findings not in scan |
push_to_jira | boolean | No | Push findings to JIRA |
tags | array | No | Tags to apply to test |
Reimport Scan (Subsequent Imports)
curl -X POST "https://defectdojo.example.com/api/v2/reimport-scan/" \
-H "Authorization: Token <api-token>" \
-F "scan_type=Trivy Scan" \
-F "file=@trivy-results.json" \
-F "test=1" \
-F "do_not_reactivate=true"Additional Parameters:
| Parameter | Type | Description |
|---|---|---|
test | integer | Existing test ID to reimport into |
do_not_reactivate | boolean | Prevent reopening previously closed findings |
Reimport Behavior:
- New findings are created
- Existing findings are updated
- Findings not in new scan are closed (unless
close_old_findings=false) - Can also auto-create context like
/import-scan/
Endpoints API
List Endpoints
curl -X GET "https://defectdojo.example.com/api/v2/endpoints/" \
-H "Authorization: Token <api-token>"Query Parameters:
product- Filter by product IDhost- Filter by hostnameprotocol- "http", "https", etc.
Create Endpoint
curl -X POST "https://defectdojo.example.com/api/v2/endpoints/" \
-H "Authorization: Token <api-token>" \
-H "Content-Type: application/json" \
-d '{
"host": "api.example.com",
"protocol": "https",
"port": 443,
"path": "/api/v1",
"product": 1
}'Test Types API
List Test Types (Scanner Types)
curl -X GET "https://defectdojo.example.com/api/v2/test_types/" \
-H "Authorization: Token <api-token>"This returns all available scanner types that can be used in scan_type parameter.
Users API
List Users
curl -X GET "https://defectdojo.example.com/api/v2/users/" \
-H "Authorization: Token <api-token>"Query Parameters:
username__contains- Partial username matchemail__contains- Partial email matchis_active- Boolean
Common Scan Types
| Scan Type | File Format |
|---|---|
Trivy Scan | JSON |
Semgrep JSON Report | JSON |
Bandit Scan | JSON |
OWASP Dependency-Check | XML |
Snyk Code Scan | JSON |
SonarQube Scan | JSON |
Checkmarx Scan | XML |
Burp REST API | JSON |
OWASP ZAP | XML |
Nessus | CSV/XML |
Qualys Scan | XML |
Gitleaks Scan | JSON |
Trufflehog Scan | JSON |
Checkov | JSON |
kube-bench Scan | JSON |
AWS Security Hub | JSON |
Azure Security Center | JSON |
Pagination
All list endpoints support pagination:
curl "https://defectdojo.example.com/api/v2/findings/?limit=100&offset=0"Response Format:
{
"count": 1234,
"next": "https://defectdojo.example.com/api/v2/findings/?limit=100&offset=100",
"previous": null,
"results": [...]
}Error Handling
Common HTTP Status Codes
| Code | Description |
|---|---|
| 200 | Success |
| 201 | Created |
| 400 | Bad Request (validation error) |
| 401 | Unauthorized (invalid/missing token) |
| 403 | Forbidden (insufficient permissions) |
| 404 | Not Found |
| 500 | Internal Server Error |
Error Response Format
{
"detail": "Error message here"
}Or for validation errors:
{
"field_name": ["Error message for this field"]
}Rate Limiting
DefectDojo doesn't have built-in rate limiting, but consider:
- Implementing client-side delays for bulk operations
- Using batch operations where available
- Monitoring API response times
Interactive Documentation
Access Swagger UI at: <your-instance>/api/v2/oa3/swagger-ui/
This provides:
- Interactive API testing
- Complete schema documentation
- Request/response examples
Azure AD SSO Configuration for DefectDojo
Azure AD App Registration Setup
Step 1: Create App Registration
1. Go to Azure Portal > Azure Active Directory > App registrations 2. Click "New registration" 3. Configure:
- Name:
DefectDojo - Supported account types: "Accounts in this organizational directory only"
- Redirect URI: Web -
https://defectdojo.dev.cafehyna.com.br/complete/azuread-tenant-oauth2/
Step 2: Configure API Permissions
Add these Application permissions (not Delegated):
| Permission | Type | Purpose |
|---|---|---|
Group.Read.All | Application | Read all groups |
GroupMember.Read.All | Application | Read group memberships |
User.Read.All | Application | Read user profiles |
Grant admin consent after adding permissions.
Step 3: Configure Token Claims
1. Go to App Registration > Token configuration 2. Add Groups claim:
- Click "Add groups claim"
- Select "All groups"
- Important: Do NOT check "Emit groups as role claims"
Step 4: Create Client Secret
1. Go to Certificates & secrets 2. Create new client secret 3. Store in Azure Key Vault as defectdojo-azuread-client-secret
DefectDojo Environment Variables
Required Variables
extraEnv:
# Enable Azure AD OAuth2 authentication
- name: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_ENABLED
value: "True"
# Azure AD Application (client) ID
- name: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_KEY
value: "79ada8c7-4270-41e8-9ea0-1e1e62afff3d"
# Azure AD Directory (tenant) ID
- name: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_TENANT_ID
value: "3f7a3df4-f85b-4ca8-98d0-08b1034e6567"
# Azure AD Client Secret (from Key Vault)
- name: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_SECRET
valueFrom:
secretKeyRef:
name: defectdojo
key: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_SECRETGroup Synchronization Variables
extraEnv:
# Enable group sync from Azure AD
- name: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_GET_GROUPS
value: "True"
# Clean up empty groups
- name: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_CLEANUP_GROUPS
value: "True"
# Filter groups by regex pattern
- name: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_GROUPS_FILTER
value: "^G-Usuarios-DefectDojo-.*"SSL/Security Variables
Required when behind a TLS-terminating proxy:
extraEnv:
# Secure cookies
- name: DD_SESSION_COOKIE_SECURE
value: "True"
- name: DD_CSRF_COOKIE_SECURE
value: "True"
# Trust proxy headers
- name: DD_SECURE_PROXY_SSL_HEADER
value: "True"
# IMPORTANT: Set to False behind NGINX Ingress to avoid redirect loops
- name: DD_SECURE_SSL_REDIRECT
value: "False"Optional SSO Behavior Variables
extraEnv:
# Auto-redirect to Azure AD login (skip username/password form)
- name: DD_SOCIAL_LOGIN_AUTO_REDIRECT
value: "True"
# Hide traditional login form
- name: DD_SOCIAL_AUTH_SHOW_LOGIN_FORM
value: "False"Azure AD Groups for DefectDojo Roles
Recommended Group Structure
| Azure AD Group Name | DefectDojo Role | Description |
|---|---|---|
G-Usuarios-DefectDojo-Superuser | Superuser | Full admin access (is_superuser=true) |
G-Usuarios-DefectDojo-Owner | Owner | Can delete products, manage members |
G-Usuarios-DefectDojo-Maintainer | Maintainer | Edit settings, delete findings |
G-Usuarios-DefectDojo-Writer | Writer | Add/edit engagements, tests, findings |
G-Usuarios-DefectDojo-Reader | Reader | View-only, add comments |
G-Usuarios-DefectDojo-APIImporter | API Importer | CI/CD pipeline scan imports |
Setting Up Group-Role Mapping
1. Create Azure AD Groups:
- Go to Azure Portal > Azure Active Directory > Groups
- Create groups matching the naming pattern above
- Add users to appropriate groups
2. Enable Group Sync in DefectDojo:
- name: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_GET_GROUPS
value: "True"
- name: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_GROUPS_FILTER
value: "^G-Usuarios-DefectDojo-.*"3. Create Matching Groups in DefectDojo:
- Go to DefectDojo > Configuration > Groups
- Create groups with exact same names as Azure AD groups
- Assign appropriate Global Role to each group
4. User Login:
- Users log in via Azure AD SSO
- Groups are synced during login
- Users inherit roles from their group memberships
Troubleshooting SSO Issues
Error: ADSTS50011 - Redirect URI Mismatch
Cause: Azure AD requires HTTPS, but DefectDojo sending HTTP redirect
Solution:
1. Verify redirect URI in Azure AD is exactly: https://defectdojo.dev.cafehyna.com.br/complete/azuread-tenant-oauth2/ 2. Set SSL environment variables:
- name: DD_SESSION_COOKIE_SECURE
value: "True"
- name: DD_CSRF_COOKIE_SECURE
value: "True"
- name: DD_SECURE_PROXY_SSL_HEADER
value: "True"Error: Groups Not Syncing
Symptoms: User logged in but shows "No group members found"
Checklist:
- [ ]
DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_GET_GROUPS=True - [ ] Azure AD App has
Group.Read.Allpermission (Application type) - [ ] Admin consent granted for API permissions
- [ ] Token configuration has Groups claim (not role claims)
- [ ] "Emit groups as role claims" is NOT enabled
- [ ] User logged out and back in after configuration change
- [ ] Matching groups exist in DefectDojo UI
Error: 403 Forbidden from Graph API
Cause: Missing or incorrect API permissions
Solution:
1. Go to Azure AD > App Registration > API Permissions 2. Add Group.Read.All as Application permission 3. Click "Grant admin consent" 4. Restart DefectDojo pods
Emergency Access
If SSO is broken and you're locked out:
https://defectdojo.dev.cafehyna.com.br/login?force_login_formThis bypasses SSO redirect and shows the standard login form.
DefectDojo CI/CD Integration Guide
Overview
DefectDojo integrates with CI/CD pipelines to automatically import security scan results. This enables:
- Automated vulnerability tracking per build/commit
- Historical trend analysis
- Build-level security gates
- Deduplication across scans
Integration Patterns
Pattern 1: Direct API Integration
The simplest approach - directly call the DefectDojo API from your pipeline.
Advantages:
- No additional dependencies
- Full control over parameters
- Works with any CI/CD system
Disadvantages:
- More code to maintain
- Error handling must be implemented
Pattern 2: Jenkins Plugin
Use the official DefectDojo Jenkins plugin for native integration.
Advantages:
- Native Jenkins integration
- UI-based configuration
- Built-in error handling
Disadvantages:
- Jenkins-specific
- Plugin updates needed
Pattern 3: Python Script
Use a reusable Python script for consistent behavior across pipelines.
Advantages:
- Reusable across projects
- Easy to customize
- Better error handling
GitLab CI Integration
Basic Integration
stages:
- security
- upload
variables:
DEFECTDOJO_URL: "https://defectdojo.example.com"
# Store DEFECTDOJO_TOKEN in CI/CD Variables
# Security Scanning Stage
trivy-scan:
stage: security
image: aquasec/trivy:latest
script:
- trivy image --format json --output trivy-results.json ${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHA}
artifacts:
paths:
- trivy-results.json
expire_in: 1 day
semgrep-scan:
stage: security
image: returntocorp/semgrep
script:
- semgrep --config=auto --json --output=semgrep-results.json .
artifacts:
paths:
- semgrep-results.json
expire_in: 1 day
# Upload to DefectDojo Stage
upload-results:
stage: upload
image: curlimages/curl:latest
dependencies:
- trivy-scan
- semgrep-scan
script:
# Upload Trivy results
- |
curl -X POST "${DEFECTDOJO_URL}/api/v2/reimport-scan/" \
-H "Authorization: Token ${DEFECTDOJO_TOKEN}" \
-F "scan_type=Trivy Scan" \
-F "file=@trivy-results.json" \
-F "product_name=${CI_PROJECT_NAME}" \
-F "engagement_name=CI/CD" \
-F "auto_create_context=true" \
-F "minimum_severity=Low" \
-F "active=true" \
-F "verified=false" \
-F "build_id=${CI_PIPELINE_ID}" \
-F "commit_hash=${CI_COMMIT_SHA}" \
-F "branch_tag=${CI_COMMIT_REF_NAME}"
# Upload Semgrep results
- |
curl -X POST "${DEFECTDOJO_URL}/api/v2/reimport-scan/" \
-H "Authorization: Token ${DEFECTDOJO_TOKEN}" \
-F "scan_type=Semgrep JSON Report" \
-F "file=@semgrep-results.json" \
-F "product_name=${CI_PROJECT_NAME}" \
-F "engagement_name=CI/CD" \
-F "auto_create_context=true"Advanced: Create Engagement Per Pipeline
.defectdojo-upload:
image: curlimages/curl:latest
script:
- |
# Create engagement for this pipeline
ENGAGEMENT_RESPONSE=$(curl -s -X POST "${DEFECTDOJO_URL}/api/v2/engagements/" \
-H "Authorization: Token ${DEFECTDOJO_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"name\": \"Pipeline-${CI_PIPELINE_ID}\",
\"product\": ${PRODUCT_ID},
\"target_start\": \"$(date +%Y-%m-%d)\",
\"target_end\": \"$(date +%Y-%m-%d)\",
\"engagement_type\": \"CI/CD\",
\"build_id\": \"${CI_PIPELINE_ID}\",
\"commit_hash\": \"${CI_COMMIT_SHA}\",
\"branch_tag\": \"${CI_COMMIT_REF_NAME}\"
}")
ENGAGEMENT_ID=$(echo $ENGAGEMENT_RESPONSE | jq -r '.id')
# Import scan with specific engagement
curl -X POST "${DEFECTDOJO_URL}/api/v2/import-scan/" \
-H "Authorization: Token ${DEFECTDOJO_TOKEN}" \
-F "scan_type=${SCAN_TYPE}" \
-F "file=@${SCAN_FILE}" \
-F "engagement=${ENGAGEMENT_ID}"GitHub Actions Integration
Basic Integration
name: Security Scan
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
env:
DEFECTDOJO_URL: ${{ secrets.DEFECTDOJO_URL }}
DEFECTDOJO_TOKEN: ${{ secrets.DEFECTDOJO_TOKEN }}
jobs:
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
format: 'json'
output: 'trivy-results.json'
severity: 'CRITICAL,HIGH,MEDIUM,LOW'
- name: Run Semgrep
uses: returntocorp/semgrep-action@v1
with:
config: auto
output: semgrep-results.json
json: true
- name: Run Gitleaks
uses: gitleaks/gitleaks-action@v2
env:
GITLEAKS_ENABLE_UPLOAD_ARTIFACT: false
continue-on-error: true
- name: Upload Trivy to DefectDojo
run: |
curl -X POST "${{ env.DEFECTDOJO_URL }}/api/v2/reimport-scan/" \
-H "Authorization: Token ${{ env.DEFECTDOJO_TOKEN }}" \
-F "scan_type=Trivy Scan" \
-F "file=@trivy-results.json" \
-F "product_name=${{ github.repository }}" \
-F "engagement_name=GitHub-Actions" \
-F "auto_create_context=true" \
-F "minimum_severity=Info" \
-F "build_id=${{ github.run_id }}" \
-F "commit_hash=${{ github.sha }}" \
-F "branch_tag=${{ github.ref_name }}"
- name: Upload Semgrep to DefectDojo
if: always()
run: |
curl -X POST "${{ env.DEFECTDOJO_URL }}/api/v2/reimport-scan/" \
-H "Authorization: Token ${{ env.DEFECTDOJO_TOKEN }}" \
-F "scan_type=Semgrep JSON Report" \
-F "file=@semgrep-results.json" \
-F "product_name=${{ github.repository }}" \
-F "engagement_name=GitHub-Actions" \
-F "auto_create_context=true"Security Gate (Fail Build on Critical Findings)
check-critical-findings:
runs-on: ubuntu-latest
needs: security-scan
steps:
- name: Check for critical findings
run: |
CRITICAL_COUNT=$(curl -s "${{ env.DEFECTDOJO_URL }}/api/v2/findings/?product_name=${{ github.repository }}&severity=Critical&active=true" \
-H "Authorization: Token ${{ env.DEFECTDOJO_TOKEN }}" | jq '.count')
if [ "$CRITICAL_COUNT" -gt 0 ]; then
echo "::error::Found $CRITICAL_COUNT critical vulnerabilities!"
exit 1
fi
echo "No critical vulnerabilities found"Jenkins Integration
Using DefectDojo Plugin
1. Install the DefectDojo plugin from Jenkins plugin manager 2. Configure in Jenkins > System Configuration:
- DefectDojo Backend URL
- API Key
- Auto Create Products/Engagements
Declarative Pipeline:
pipeline {
agent any
environment {
DEFECTDOJO_URL = 'https://defectdojo.example.com'
DEFECTDOJO_API_KEY = credentials('defectdojo-api-key')
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Security Scans') {
parallel {
stage('Trivy Scan') {
steps {
sh 'trivy fs --format json -o trivy-results.json .'
}
}
stage('Semgrep Scan') {
steps {
sh 'semgrep --config=auto --json -o semgrep-results.json .'
}
}
stage('Dependency Check') {
steps {
sh 'dependency-check --scan . --format JSON -o dependency-check-results.json'
}
}
}
}
stage('Upload to DefectDojo') {
steps {
// Using DefectDojo Plugin
defectDojoPublisher(
artifact: 'trivy-results.json',
productName: env.JOB_NAME,
scanType: 'Trivy Scan',
engagementName: "Build-${BUILD_NUMBER}"
)
defectDojoPublisher(
artifact: 'semgrep-results.json',
productName: env.JOB_NAME,
scanType: 'Semgrep JSON Report',
engagementName: "Build-${BUILD_NUMBER}"
)
defectDojoPublisher(
artifact: 'dependency-check-results.json',
productName: env.JOB_NAME,
scanType: 'Dependency Check Scan',
engagementName: "Build-${BUILD_NUMBER}"
)
}
}
stage('Security Gate') {
steps {
script {
def response = httpRequest(
url: "${DEFECTDOJO_URL}/api/v2/findings/?product_name=${env.JOB_NAME}&severity=Critical&active=true",
customHeaders: [[name: 'Authorization', value: "Token ${DEFECTDOJO_API_KEY}"]]
)
def findings = readJSON text: response.content
if (findings.count > 0) {
error "Found ${findings.count} critical vulnerabilities! Build failed."
}
}
}
}
}
post {
always {
archiveArtifacts artifacts: '*-results.json', fingerprint: true
}
}
}Using Shell Commands (Without Plugin)
stage('Upload to DefectDojo') {
steps {
withCredentials([string(credentialsId: 'defectdojo-token', variable: 'DD_TOKEN')]) {
sh """
curl -X POST "${DEFECTDOJO_URL}/api/v2/reimport-scan/" \
-H "Authorization: Token \${DD_TOKEN}" \
-F "scan_type=Trivy Scan" \
-F "file=@trivy-results.json" \
-F "product_name=${env.JOB_NAME}" \
-F "engagement_name=Build-${BUILD_NUMBER}" \
-F "auto_create_context=true" \
-F "build_id=${BUILD_NUMBER}" \
-F "commit_hash=${GIT_COMMIT}"
"""
}
}
}Azure DevOps Integration
trigger:
- main
- develop
pool:
vmImage: 'ubuntu-latest'
variables:
- group: defectdojo-credentials
stages:
- stage: SecurityScan
jobs:
- job: ScanAndUpload
steps:
- task: Bash@3
displayName: 'Run Trivy Scan'
inputs:
targetType: 'inline'
script: |
docker run --rm -v $(pwd):/project aquasec/trivy:latest \
fs --format json -o /project/trivy-results.json /project
- task: Bash@3
displayName: 'Upload to DefectDojo'
inputs:
targetType: 'inline'
script: |
curl -X POST "$(DEFECTDOJO_URL)/api/v2/reimport-scan/" \
-H "Authorization: Token $(DEFECTDOJO_TOKEN)" \
-F "scan_type=Trivy Scan" \
-F "file=@trivy-results.json" \
-F "product_name=$(Build.Repository.Name)" \
-F "engagement_name=AzureDevOps" \
-F "auto_create_context=true" \
-F "build_id=$(Build.BuildId)" \
-F "commit_hash=$(Build.SourceVersion)" \
-F "branch_tag=$(Build.SourceBranchName)"Python Upload Script
Reusable script for consistent uploads across pipelines:
#!/usr/bin/env python3
"""
DefectDojo CI/CD Upload Script
Usage: python upload_to_defectdojo.py --url <url> --token <token> --product <name> --scan-type <type> --file <path>
"""
import argparse
import requests
import sys
import os
from datetime import date
def upload_scan(args):
"""Upload scan results to DefectDojo."""
headers = {
'Authorization': f'Token {args.token}'
}
# Prepare form data
data = {
'scan_type': args.scan_type,
'minimum_severity': args.minimum_severity or 'Info',
'active': 'true',
'verified': 'false',
'auto_create_context': 'true',
'close_old_findings': 'true',
}
# Add product/engagement context
if args.product:
data['product_name'] = args.product
if args.engagement:
data['engagement_name'] = args.engagement
if args.test_title:
data['test_title'] = args.test_title
# Add CI/CD metadata
if args.build_id:
data['build_id'] = args.build_id
if args.commit_hash:
data['commit_hash'] = args.commit_hash
if args.branch:
data['branch_tag'] = args.branch
# Upload file
with open(args.file, 'rb') as f:
files = {'file': (os.path.basename(args.file), f)}
endpoint = f'{args.url.rstrip("/")}/api/v2/reimport-scan/'
try:
response = requests.post(
endpoint,
headers=headers,
data=data,
files=files,
timeout=300
)
response.raise_for_status()
result = response.json()
print(f"Successfully uploaded scan results!")
print(f"Test ID: {result.get('test', 'N/A')}")
print(f"Findings created: {result.get('statistics', {}).get('created', 0)}")
print(f"Findings closed: {result.get('statistics', {}).get('closed', 0)}")
return 0
except requests.exceptions.HTTPError as e:
print(f"HTTP Error: {e}")
print(f"Response: {e.response.text}")
return 1
except requests.exceptions.RequestException as e:
print(f"Request Error: {e}")
return 1
def main():
parser = argparse.ArgumentParser(description='Upload scan results to DefectDojo')
parser.add_argument('--url', required=True, help='DefectDojo URL')
parser.add_argument('--token', required=True, help='API Token')
parser.add_argument('--product', required=True, help='Product name')
parser.add_argument('--scan-type', required=True, help='Scanner type (e.g., "Trivy Scan")')
parser.add_argument('--file', required=True, help='Scan results file path')
parser.add_argument('--engagement', default='CI/CD', help='Engagement name')
parser.add_argument('--test-title', help='Custom test title')
parser.add_argument('--minimum-severity', default='Info',
choices=['Info', 'Low', 'Medium', 'High', 'Critical'],
help='Minimum severity to import')
parser.add_argument('--build-id', help='CI/CD build ID')
parser.add_argument('--commit-hash', help='Git commit hash')
parser.add_argument('--branch', help='Git branch name')
args = parser.parse_args()
sys.exit(upload_scan(args))
if __name__ == '__main__':
main()Usage:
python upload_to_defectdojo.py \
--url https://defectdojo.example.com \
--token $DEFECTDOJO_TOKEN \
--product "MyApp" \
--scan-type "Trivy Scan" \
--file trivy-results.json \
--build-id $CI_PIPELINE_ID \
--commit-hash $CI_COMMIT_SHA \
--branch $CI_BRANCHBest Practices
1. Use Reimport for Continuous Scanning
# Always use reimport-scan for CI/CD
/api/v2/reimport-scan/ # Preferred - handles deduplication
# Only use import-scan for first-time imports
/api/v2/import-scan/ # Creates new test each time2. Enable Auto-Create Context
-F "auto_create_context=true"This automatically creates Product/Engagement if they don't exist.
3. Track Build Metadata
-F "build_id=${BUILD_ID}" \
-F "commit_hash=${GIT_COMMIT}" \
-F "branch_tag=${GIT_BRANCH}" \
-F "source_code_management_uri=${GIT_URL}"4. Set Appropriate Minimum Severity
# Development: All findings
-F "minimum_severity=Info"
# Production: Focus on actionable items
-F "minimum_severity=Medium"5. Implement Security Gates
Query DefectDojo API to fail builds on critical findings:
CRITICAL=$(curl -s "${DEFECTDOJO_URL}/api/v2/findings/?severity=Critical&active=true&product_name=${PRODUCT}" \
-H "Authorization: Token ${TOKEN}" | jq '.count')
if [ "$CRITICAL" -gt 0 ]; then
echo "Build failed: $CRITICAL critical vulnerabilities found"
exit 1
fi6. Use Consistent Product Naming
# Use repository name for consistency
-F "product_name=${CI_PROJECT_NAME}"
-F "product_name=${{ github.repository }}"
-F "product_name=${env.JOB_NAME}"7. Handle Upload Failures Gracefully
# Continue pipeline even if upload fails
- name: Upload to DefectDojo
continue-on-error: true
run: |
curl ... || echo "Warning: Failed to upload to DefectDojo"DefectDojo Helm Chart Values Reference
Chart Information
| Property | Value |
|---|---|
| Chart Repository | https://raw.githubusercontent.com/DefectDojo/django-DefectDojo/helm-charts |
| Chart Name | defectdojo |
| Current Version | 1.8.3 |
| App Version | 2.52.3 |
Core Configuration
Host Configuration
# Primary hostname
host: defectdojo.dev.cafehyna.com.br
# Full site URL (used for OAuth redirect URIs)
siteUrl: https://defectdojo.dev.cafehyna.com.br
# Alternative hostnames (internal access)
alternativeHosts:
- defectdojo.cafehyna-dev.internalSecret Configuration
# Disable Helm-managed secrets (use CSI driver instead)
createSecret: false
# Disable hooks for ArgoCD compatibility
disableHooks: true
# PostgreSQL secret
createPostgresqlSecret: true
# Redis secret
createRedisSecret: trueDjango Configuration
django:
# Number of replicas
replicas: 1
# Ingress configuration
ingress:
enabled: true
activateTLS: true
className: nginx
secretName: defectdojo-tls
annotations:
external-dns.alpha.kubernetes.io/hostname: defectdojo.dev.cafehyna.com.br
cert-manager.io/cluster-issuer: letsencrypt-staging-cloudflare
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: 100m
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
# uWSGI configuration
uwsgi:
enable: true
livenessProbe:
initialDelaySeconds: 120
periodSeconds: 30
timeoutSeconds: 10
failureThreshold: 6
readinessProbe:
initialDelaySeconds: 60
periodSeconds: 15
timeoutSeconds: 10
resources:
requests:
cpu: 250m
memory: 1Gi
limits:
memory: 1Gi
# Security context
securityContext:
enabled: true
runAsUser: 1001
runAsGroup: 1001
fsGroup: 1001
runAsNonRoot: true
allowPrivilegeEscalation: false
# Autoscaling (disabled for dev)
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 3
# Spot node tolerations
tolerations:
- key: kubernetes.azure.com/scalesetpriority
operator: Equal
value: spot
effect: NoSchedule
# Node affinity
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: agentpool
operator: In
values:
- cafedevspotCelery Configuration
Important: Celery Beat must remain a singleton (replicas: 1) to prevent duplicate task execution.
celery:
beat:
enabled: true
replicas: 1 # MUST be 1 - do not scale
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
memory: 256Mi
tolerations:
- key: kubernetes.azure.com/scalesetpriority
operator: Equal
value: spot
effect: NoSchedule
worker:
enabled: true
replicas: 1
logLevel: INFO
concurrency: 2
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
memory: 512Mi
autoscaling:
enabled: false
tolerations:
- key: kubernetes.azure.com/scalesetpriority
operator: Equal
value: spot
effect: NoSchedulePostgreSQL Configuration
postgresql:
enabled: true
auth:
username: defectdojo
database: defectdojo
existingSecret: defectdojo-postgresql-secret
secretKeys:
adminPasswordKey: postgres-password
userPasswordKey: password
primary:
persistence:
enabled: true
size: 10Gi
storageClass: managed-premium-zrs
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
memory: 512Mi
tolerations:
- key: kubernetes.azure.com/scalesetpriority
operator: Equal
value: spot
effect: NoSchedule
metrics:
enabled: true
serviceMonitor:
enabled: true
namespace: defectdojo
interval: 30sRedis Configuration
redis:
enabled: true
architecture: standalone
auth:
enabled: true
existingSecret: defectdojo-redis-secret
existingSecretPasswordKey: redis-password
master:
persistence:
enabled: true
size: 4Gi
storageClass: managed-premium-zrs
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
memory: 256Mi
tolerations:
- key: kubernetes.azure.com/scalesetpriority
operator: Equal
value: spot
effect: NoSchedule
metrics:
enabled: true
serviceMonitor:
enabled: truePersistent Volume Configuration
persistentVolume:
enabled: true
size: 20Gi
accessMode: ReadWriteMany
storageClass: azurefile-csi-premium-zrsEnvironment Variables
Core Django Settings
extraEnv:
- name: DD_DEBUG
value: "False"
- name: DD_TIME_ZONE
value: America/Sao_Paulo
- name: DD_LANGUAGE_CODE
value: pt-br
- name: DD_ALLOWED_HOSTS
value: defectdojo.dev.cafehyna.com.br,defectdojo.cafehyna-dev.internal,localhost
- name: DD_CSRF_TRUSTED_ORIGINS
value: https://defectdojo.dev.cafehyna.com.brSecurity Settings
extraEnv:
- name: DD_SESSION_COOKIE_HTTPONLY
value: "True"
- name: DD_CSRF_COOKIE_HTTPONLY
value: "True"
- name: DD_SECURE_SSL_REDIRECT
value: "False" # False when behind TLS-terminating proxy
- name: DD_SECURE_PROXY_SSL_HEADER
value: "True"
- name: DD_SECURE_BROWSER_XSS_FILTER
value: "True"Metrics Settings
extraEnv:
- name: DD_DJANGO_METRICS_ENABLED
value: "True"
- name: DD_CELERY_METRICS_ENABLED
value: "True"CSI Volume Mounts
# Mount Azure Key Vault secrets
extraVolumes:
- name: secrets-store
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: "defectdojo-secrets"
extraVolumeMounts:
- name: secrets-store
mountPath: "/mnt/secrets-store"
readOnly: trueHigh Availability Configuration
For production deployments:
django:
replicas: 3
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 5
celery:
worker:
replicas: 3
autoscaling:
enabled: true
postgresql:
replication:
enabled: true
slaveReplicas: 2
redis:
architecture: replication
replicas: 3Service Account
serviceAccount:
create: true
name: defectdojoPod Disruption Budget
podDisruptionBudget:
enabled: true
minAvailable: 1Monitoring
monitoring:
enabled: true
createServiceMonitor: true
metrics:
enabled: true
serviceMonitor:
enabled: true
interval: 30s
scrapeTimeout: 10sDefectDojo Troubleshooting Guide
Authentication Issues
Azure AD SSO Login Fails
Error: ADSTS50011 - Redirect URI Mismatch
Error Message:
ADSTS50011: The redirect URI 'http://xxxxx/azuread-tenant-oauth2/' specified in the request does not matchCause: Azure AD requires HTTPS but DefectDojo is sending HTTP redirect URI
Solutions:
1. Verify Azure AD Redirect URI:
- Must be exactly:
https://defectdojo.dev.cafehyna.com.br/complete/azuread-tenant-oauth2/ - Note the
/complete/in the path
2. Set SSL Environment Variables:
extraEnv:
- name: DD_SESSION_COOKIE_SECURE
value: "True"
- name: DD_CSRF_COOKIE_SECURE
value: "True"
- name: DD_SECURE_PROXY_SSL_HEADER
value: "True"3. Restart pods after changes:
KUBECONFIG=~/.kube/aks-rg-hypera-cafehyna-dev-config \
kubectl rollout restart deployment/defectdojo-django -n defectdojoError: ERR_TOO_MANY_REDIRECTS
Cause: DD_SECURE_SSL_REDIRECT=True behind TLS-terminating proxy causes infinite redirect loop
Solution:
# Set to False when behind NGINX Ingress (it handles SSL redirect)
- name: DD_SECURE_SSL_REDIRECT
value: "False"User Locked Out / SSO Broken
Emergency Access: Navigate to: https://defectdojo.dev.cafehyna.com.br/login?force_login_form
This bypasses SSO redirect and shows standard username/password login.
---
Group Synchronization Issues
Groups Not Syncing from Azure AD
Symptoms:
- User logs in via Azure AD
- User profile shows "No group members found"
- User doesn't have expected permissions
Diagnostic Steps:
1. Check Configuration:
KUBECONFIG=~/.kube/aks-rg-hypera-cafehyna-dev-config \
kubectl get deployment defectdojo-django -n defectdojo -o yaml | \
grep -A1 "DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_GET_GROUPS"Should show value: "True"
2. Check Azure AD API Permissions:
- Go to Azure Portal > App Registrations > DefectDojo > API Permissions
- Verify
Group.Read.Allis listed as Application permission - Verify "Admin consent granted" shows green checkmark
3. Check Azure AD Token Configuration:
- Go to Token configuration
- Verify "Groups claim" is added
- Verify "Emit groups as role claims" is NOT enabled
4. Check Pod Logs for Graph API Errors:
KUBECONFIG=~/.kube/aks-rg-hypera-cafehyna-dev-config \
kubectl logs -n defectdojo -l app.kubernetes.io/name=defectdojo -c uwsgi | \
grep -i "graph\|group\|403"Solutions:
1. Grant API Permissions:
- Add
Group.Read.All(Application type) - Add
GroupMember.Read.All(Application type) - Click "Grant admin consent"
2. Fix Token Configuration:
- Add Groups claim if missing
- Disable "Emit groups as role claims"
3. Create Matching Groups in DefectDojo:
- Go to Configuration > Groups
- Create group with exact Azure AD group name
- Assign Global Role
4. User Must Re-login:
- Groups sync only at login time
- User must log out and log back in
Error: 403 Forbidden from Microsoft Graph API
Cause: Missing or incorrect API permissions
Check Current Permissions:
az ad app show --id 79ada8c7-4270-41e8-9ea0-1e1e62afff3d \
--query "requiredResourceAccess" -o jsonSolution:
1. Go to Azure AD > App Registration > API Permissions 2. Add Group.Read.All as Application permission (not Delegated) 3. Click "Grant admin consent for [tenant]" 4. Wait 5-10 minutes for propagation 5. Restart DefectDojo pods
---
CSRF Issues
Error: "CSRF verification failed"
Symptoms: 403 error after form submission
Common Causes:
1. Missing CSRF Trusted Origins:
- name: DD_CSRF_TRUSTED_ORIGINS
value: https://defectdojo.dev.cafehyna.com.br2. Stale CSRF Cookie:
- Clear browser cookies
- Use incognito/private window
3. Proxy Not Forwarding Headers:
# Trust X-Forwarded-Proto header
- name: DD_SECURE_PROXY_SSL_HEADER
value: "True"---
Pod/Container Issues
Pods Stuck in ContainerCreating
Check Events:
KUBECONFIG=~/.kube/aks-rg-hypera-cafehyna-dev-config \
kubectl describe pod -n defectdojo -l app.kubernetes.io/name=defectdojo | tail -20Common Causes:
1. CSI Driver Secret Mount Failure:
- Check SecretProviderClass exists
- Verify Key Vault permissions
2. PVC Not Bound:
KUBECONFIG=~/.kube/aks-rg-hypera-cafehyna-dev-config \
kubectl get pvc -n defectdojo3. Node Scheduling Issues:
- Check tolerations match node taints
- Verify nodeSelector/affinity
Pods CrashLoopBackOff
Check Logs:
KUBECONFIG=~/.kube/aks-rg-hypera-cafehyna-dev-config \
kubectl logs -n defectdojo -l app.kubernetes.io/name=defectdojo -c uwsgi --previousCommon Causes:
1. Database Connection Failed:
- Check PostgreSQL pod is running
- Verify database credentials
2. Redis Connection Failed:
- Check Redis pod is running
- Verify Redis password
3. Missing Environment Variables:
- Check all required secrets exist
Liveness/Readiness Probe Failures
Symptoms: Pod restarts frequently
Solutions:
1. Increase Initial Delay:
django:
uwsgi:
livenessProbe:
initialDelaySeconds: 180
readinessProbe:
initialDelaySeconds: 1202. Check Resource Limits:
- Pod may be OOMKilled
- Increase memory limits
---
Database Issues
Database Migration Failures
Check Migration Status:
KUBECONFIG=~/.kube/aks-rg-hypera-cafehyna-dev-config \
kubectl logs -n defectdojo -l app.kubernetes.io/component=initializerSolutions:
1. Run Migrations Manually:
KUBECONFIG=~/.kube/aks-rg-hypera-cafehyna-dev-config \
kubectl exec -it -n defectdojo deployment/defectdojo-django -c uwsgi -- \
python manage.py migrate2. Check Database Connectivity:
KUBECONFIG=~/.kube/aks-rg-hypera-cafehyna-dev-config \
kubectl exec -it -n defectdojo deployment/defectdojo-django -c uwsgi -- \
python manage.py dbshell---
Secrets Issues
Kubernetes Secret Not Created
Symptoms: Secret referenced but doesn't exist
Cause: CSI driver only creates secrets when a pod mounts the volume
Solution:
1. Verify secret-sync-pod is running 2. Check SecretProviderClass is correctly configured 3. Pod must mount the CSI volume for secrets to sync
Check Secret Sync Pod:
KUBECONFIG=~/.kube/aks-rg-hypera-cafehyna-dev-config \
kubectl get pods -n defectdojo | grep secret-syncKey Vault 403 Forbidden
Check Managed Identity Permissions:
az keyvault show --name kv-cafehyna-dev-hlg --query "properties.accessPolicies"Grant Permissions:
az keyvault set-policy \
--name kv-cafehyna-dev-hlg \
--object-id <managed-identity-object-id> \
--secret-permissions get list---
ArgoCD Sync Issues
App Out of Sync
Common Causes:
1. Job podReplacementPolicy Field:
- Already handled in ApplicationSet with
ignoreDifferences
2. Replica Count Drift:
- HPA changes replica counts
- Handled with
ignoreDifferences
Sync Failed
Check ArgoCD Logs:
kubectl logs -n argocd -l app.kubernetes.io/name=argocd-application-controllerManual Sync:
argocd app sync cafehyna-dev-defectdojo---
Useful Diagnostic Commands
Overall Health Check
# Pod status
KUBECONFIG=~/.kube/aks-rg-hypera-cafehyna-dev-config \
kubectl get pods -n defectdojo
# Recent events
KUBECONFIG=~/.kube/aks-rg-hypera-cafehyna-dev-config \
kubectl get events -n defectdojo --sort-by='.lastTimestamp' | tail -20
# Secret status
KUBECONFIG=~/.kube/aks-rg-hypera-cafehyna-dev-config \
kubectl get secrets -n defectdojo
# PVC status
KUBECONFIG=~/.kube/aks-rg-hypera-cafehyna-dev-config \
kubectl get pvc -n defectdojoApplication Logs
# Django/uWSGI logs
KUBECONFIG=~/.kube/aks-rg-hypera-cafehyna-dev-config \
kubectl logs -n defectdojo -l app.kubernetes.io/name=defectdojo -c uwsgi -f
# Celery worker logs
KUBECONFIG=~/.kube/aks-rg-hypera-cafehyna-dev-config \
kubectl logs -n defectdojo -l app.kubernetes.io/component=celery-worker -f
# Celery beat logs
KUBECONFIG=~/.kube/aks-rg-hypera-cafehyna-dev-config \
kubectl logs -n defectdojo -l app.kubernetes.io/component=celery-beat -fRestart Services
# Restart Django
KUBECONFIG=~/.kube/aks-rg-hypera-cafehyna-dev-config \
kubectl rollout restart deployment/defectdojo-django -n defectdojo
# Restart all components
KUBECONFIG=~/.kube/aks-rg-hypera-cafehyna-dev-config \
kubectl rollout restart deployment -n defectdojo