28 KiB
Charybdis Plugin Configuration Guide
Complete reference for configuring and using the Charybdis plugin system.
Table of Contents
- Overview
- Plugin System Architecture
- Configuration File Structure
- Field Mapping System
- Entity Resolution
- Plugin Configuration Reference
- Environment Variables
- Best Practices
- Troubleshooting
- Examples
Overview
Charybdis plugins extend the core platform with integrations to external tools and services. Plugins react to entity lifecycle events (Create, Update, Delete) and automatically provision or sync resources in external systems.
Plugin Types
Event-Driven Plugins (Current Implementation):
- React to entity lifecycle events
- Push data to external systems
- One-way sync: Charybdis → External Tool
Sync Plugins (Future):
- Pull data from external systems on schedule
- Create/update entities in Charybdis
- One-way sync: External Tool → Charybdis
Plugin System Architecture
┌─────────────────────────────────────────┐
│ Charybdis Core │
│ ┌────────────────────────────────┐ │
│ │ Entity Repository │ │
│ │ (PostgreSQL) │ │
│ └──────────┬─────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────┐ │
│ │ Event Bus │ │
│ │ (In-Memory / Future: Redis) │ │
│ └──────────┬─────────────────────┘ │
│ │ EntityEvent │
│ │ (Created/Updated/Deleted) │
└─────────────┼────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Event Dispatcher │
│ (Routes events to plugins) │
└──────────┬──────────────────────────────┘
│
├─────────────────┐
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ DefectDojo │ │ Future Plugins │
│ Plugin │ │ (DependencyTrack)│
│ │ │ │
│ ┌──────────────┐ │ └──────────────────┘
│ │ProductHandler│ │
│ │UserHandler │ │
│ │... │ │
│ └──────────────┘ │
└─────────┬────────┘
│ HTTP/REST
▼
┌──────────────────┐
│ External Tool │
│ (DefectDojo) │
└──────────────────┘
Key Components
- Plugin: Top-level plugin interface
- ResourceHandler: Handles specific resource types (e.g., Products, Users)
- FieldMapper: Maps Charybdis entity fields to external tool fields
- Event Dispatcher: Routes entity events to appropriate handlers
- Plugin Manager: Manages plugin lifecycle and registration
Configuration File Structure
Plugin configuration lives in your main config.toml file under the [plugins.<plugin_name>] section.
Basic Structure
[plugins.<plugin_name>]
enabled = true # Enable/disable plugin
base_url = "${API_URL}" # External tool API URL
api_token = "${API_TOKEN}" # Authentication token
<plugin_specific_options> # Plugin-specific settings
[plugins.<plugin_name>.field_mappings.<resource_type>]
<field_name> = <mapping> # Field mappings per resource type
Configuration Sections
1. Plugin Enable/Disable
[plugins.defectdojo]
enabled = true # or false to disable
When disabled, the plugin:
- Will NOT be compiled into the binary
- Will NOT react to entity events
- Will NOT appear in plugin registry
2. Connection Settings
[plugins.defectdojo]
base_url = "${DEFECTDOJO_URL}"
api_token = "${DEFECTDOJO_API_TOKEN}"
Security Best Practice: Always use environment variables for sensitive data (tokens, passwords, secrets).
3. Plugin-Specific Options
Each plugin may have unique configuration options:
[plugins.defectdojo]
default_product_type_id = 1 # Default product type for new products
auto_create_users = true # Auto-create missing users
auto_create_product_types = false # Require manual product type creation
4. Field Mappings
Field mappings define how Charybdis entity fields map to external tool API fields.
[plugins.defectdojo.field_mappings.product]
name = "metadata.name" # Direct field mapping
description = "metadata.description" # Direct field mapping
business_criticality = { value = "high" } # Static value
tags = "metadata.tags" # Array mapping
Field Mapping System
Mapping Types
The field mapping system supports three types of mappings:
1. Direct Field Mapping (String)
Maps a field from the Charybdis entity using dot notation:
name = "metadata.name"
description = "metadata.description"
email = "spec.profile.email"
tags = "metadata.tags"
Syntax: "<path.to.field>"
Examples:
"metadata.name"→entity.metadata.name"spec.owner"→entity.spec.owner"metadata.annotations.version"→entity.metadata.annotations["version"]
2. Static Value Mapping (Object with value key)
Provides a static value regardless of entity content:
business_criticality = { value = "high" }
platform = { value = "web" }
is_active = { value = true }
priority = { value = 100 }
lifecycle = { value = "production" }
Syntax: { value = <any_json_value> }
Supported Types:
- String:
{ value = "text" } - Boolean:
{ value = true } - Number:
{ value = 42 } - Object:
{ value = { key = "value" } } - Array:
{ value = ["item1", "item2"] }
3. Complex Mapping with Entity Resolution (Object)
Resolves entity references and extracts data from related entities:
product_manager = {
from = "spec.owner",
resolve_entity = "User",
extract = "annotations.defectdojo.com/user-id"
}
Syntax:
<field> = {
from = "<source_field>", # Source field path
resolve_entity = "<entity_kind>", # Entity kind to resolve to
extract = "<field_to_extract>", # Field to extract from resolved entity
resolve_array = <true|false>, # Optional: resolve array of entities
lookup_entity = "<entity_kind>" # Optional: lookup linked entity
}
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
from |
string | ✓ | Source field path in dot notation |
resolve_entity |
string | ✓ | Entity kind to resolve (User, Component, etc.) |
extract |
string | ✓ | Field path to extract from resolved entity |
resolve_array |
boolean | ✗ | Set to true if from contains array of entity IDs |
lookup_entity |
string | ✗ | Find linked entity by source ID |
Entity Resolution
Entity resolution is the most powerful feature of the field mapping system. It allows plugins to navigate entity relationships and extract data from related entities.
How Entity Resolution Works
1. Start with source entity (e.g., Component)
entity.spec.owner = "user:john.doe"
2. Extract source value
from = "spec.owner" → "user:john.doe"
3. Resolve entity reference
resolve_entity = "User" → Query for User entity with id "john.doe"
4. Extract target field
extract = "annotations.defectdojo.com/user-id" → "123"
5. Result
product_manager = 123
Single Entity Resolution
Resolve one entity reference:
[plugins.defectdojo.field_mappings.product]
product_manager = {
from = "spec.owner", # Component.spec.owner = "user:john.doe"
resolve_entity = "User", # Find User entity
extract = "annotations.defectdojo.com/user-id" # Get DD user ID → "123"
}
Array Entity Resolution
Resolve multiple entity references:
[plugins.defectdojo.field_mappings.product_member]
user_ids = {
from = "spec.members", # Group.spec.members = ["user:john", "user:jane"]
resolve_entity = "User", # Find each User entity
extract = "annotations.defectdojo.com/user-id", # Get DD user ID for each
resolve_array = true # Process as array
}
Result: user_ids = [123, 124]
Linked Entity Lookup
Find an entity linked to the source entity:
engagement_product = {
from = "id", # Start with engagement entity ID
lookup_entity = "Component", # Find Component where...
extract = "annotations.defectdojo.com/product-id" # Component links to this engagement
}
This searches for a Component entity that references the source entity.
Nested Entity Resolution
Chain multiple resolutions:
team_lead_email = {
from = "spec.owner", # Component.spec.owner = "group:platform-team"
resolve_entity = "Group", # Find Group entity
extract = "spec.parent" # Get parent = "user:tech-lead"
}
# Then in a second mapping:
lead_id = {
from = "spec.parent", # From previous resolution
resolve_entity = "User", # Find User entity
extract = "annotations.defectdojo.com/user-id"
}
Entity Resolution Error Handling
The field mapper handles errors gracefully:
- Entity not found: Logs warning, field is omitted
- Annotation missing: Logs warning, field is omitted
- Invalid entity reference: Logs warning, field is omitted
- Array with missing entities: Logs warning, includes found entities only
In map_all() mode: Failed field mappings log warnings but don't fail entire operation.
Plugin Configuration Reference
DefectDojo Plugin
Complete configuration reference for the DefectDojo plugin.
Connection Settings
[plugins.defectdojo]
enabled = true
base_url = "${DEFECTDOJO_URL}"
api_token = "${DEFECTDOJO_API_TOKEN}"
Plugin Options
[plugins.defectdojo]
# Default product type ID for new products (required if not mapped)
default_product_type_id = 1
# Automatically create users in DefectDojo when referenced
# If false, referenced users must exist in DefectDojo
auto_create_users = true
# Automatically create product types in DefectDojo
# If false, product types must be created manually
auto_create_product_types = false
Resource Type: Product
Maps Component entities to DefectDojo Products.
[plugins.defectdojo.field_mappings.product]
# Required fields
name = "metadata.name" # Product name
description = "metadata.description" # Product description
product_type_id = { value = 1 } # Product type ID
# Optional fields
tags = "metadata.tags" # Product tags (array)
business_criticality = { value = "high" } # very high, high, medium, low, very low, none
platform = { value = "web" } # web, mobile, desktop, iot, etc.
lifecycle = { value = "production" } # production, development, retirement
origin = { value = "internal" } # internal, external, third party
user_records = { value = 1000000 } # Number of user records
revenue = { value = "1000000" } # Revenue amount
external_audience = { value = true } # Accessible to external users
internet_accessible = { value = true } # Accessible via internet
# User references (with entity resolution)
product_manager = {
from = "spec.owner",
resolve_entity = "User",
extract = "annotations.defectdojo.com/user-id"
}
technical_contact = {
from = "metadata.annotations.technical-contact",
resolve_entity = "User",
extract = "annotations.defectdojo.com/user-id"
}
team_manager = {
from = "metadata.annotations.team-manager",
resolve_entity = "User",
extract = "annotations.defectdojo.com/user-id"
}
Resource Type: User
Maps User entities to DefectDojo Users.
[plugins.defectdojo.field_mappings.user]
# Required fields
username = "metadata.name" # Username (unique)
email = "spec.profile.email" # Email address (unique)
# Optional fields
first_name = "spec.profile.displayName" # First name
last_name = "spec.profile.displayName" # Last name (can use same field)
is_active = { value = true } # User active status
Resource Type: Product Type
Maps System entities to DefectDojo Product Types.
[plugins.defectdojo.field_mappings.product_type]
# Required fields
name = "metadata.name" # Product type name
# Optional fields
description = "metadata.description" # Product type description
critical_product = { value = false } # Requires extra review
key_product = { value = true } # Important but not critical
Resource Type: Product Member
Maps Group entities to DefectDojo Product Members (user-product-role assignments).
Note: Only Group entities with specific structure are processed.
[plugins.defectdojo.field_mappings.product_member]
# Product reference (resolve from Group parent)
product_id = {
from = "spec.parent", # Group.spec.parent = "component:my-service"
resolve_entity = "Component", # Find Component entity
extract = "annotations.defectdojo.com/product-id" # Get DD product ID
}
# User reference (resolve from Group members)
user_id = {
from = "spec.members", # Group.spec.members = ["user:john", "user:jane"]
resolve_entity = "User", # Find User entities
extract = "annotations.defectdojo.com/user-id", # Get DD user IDs
resolve_array = true # Process as array
}
# Role assignment
role_name = { value = "Reader" } # Owner, Maintainer, Reader, Writer, API_Importer
DefectDojo Roles:
- Owner: Full control over product
- Maintainer: Can modify product settings
- Writer: Can add findings and tests
- Reader: Read-only access
- API_Importer: Can import findings via API
Resource Type: Engagement
Maps Resource entities to DefectDojo Engagements (security assessments).
[plugins.defectdojo.field_mappings.engagement]
# Required fields
name = "metadata.name" # Engagement name
description = "metadata.description" # Engagement description
# Product reference
product_id = {
from = "spec.owner", # Resource.spec.owner = "component:my-service"
resolve_entity = "Component", # Find Component entity
extract = "annotations.defectdojo.com/product-id"
}
# Date fields (ISO 8601 format: YYYY-MM-DD)
target_start = "spec.target_start" # Start date
target_end = "spec.target_end" # End date
# Optional fields
status = { value = "In Progress" } # Not Started, In Progress, Completed, Cancelled
engagement_type = { value = "CI/CD" } # Interactive, CI/CD, etc.
# Lead user reference
lead_id = {
from = "spec.dependsOn", # Resource.spec.dependsOn = ["user:lead"]
resolve_entity = "User",
extract = "annotations.defectdojo.com/user-id"
}
# Version control fields
version = "metadata.annotations.version"
commit_hash = "metadata.annotations.commit_hash"
branch_tag = "metadata.annotations.branch"
build_id = "metadata.annotations.build_id"
source_code_management_uri = "metadata.annotations.repo_url"
# Test type flags
deduplication_on_engagement = { value = true }
threat_model = { value = false }
api_test = { value = true }
pen_test = { value = false }
check_list = { value = false }
Environment Variables
Required Variables
# DefectDojo Plugin
export DEFECTDOJO_URL="https://defectdojo.example.com"
export DEFECTDOJO_API_TOKEN="your-api-token-here"
# Database
export DATABASE_URL="postgresql://user:pass@localhost/charybdis"
# Optional: Telemetry
export OTLP_ENDPOINT="http://localhost:4317"
Obtaining DefectDojo API Token
- Log into DefectDojo UI
- Go to User Profile → API Key
- Click Generate or copy existing key
- Set environment variable:
export DEFECTDOJO_API_TOKEN="<token>"
Security Considerations
Never commit secrets to git:
- ✓ Use environment variables for tokens/passwords
- ✓ Add
.envto.gitignore - ✓ Use secret management (HashiCorp Vault, AWS Secrets Manager)
- ✗ Don't hardcode secrets in
config.toml
Best Practices
1. Field Mapping Design
Start Simple:
# Start with required fields only
name = "metadata.name"
description = "metadata.description"
Add Optional Fields Gradually:
# Add optional fields as needed
tags = "metadata.tags"
business_criticality = { value = "medium" }
Use Entity Resolution Last:
# Complex mappings last (requires other entities to exist)
product_manager = {
from = "spec.owner",
resolve_entity = "User",
extract = "annotations.defectdojo.com/user-id"
}
2. Entity Creation Order
For entity resolution to work, create entities in this order:
- Users (no dependencies)
- Product Types (no dependencies)
- Components (may reference Users)
- Groups (references Components and Users)
- Resources/Engagements (references Components and Users)
3. Testing Strategy
Test in Isolation:
- Create a test Component without references
- Verify Product created in DefectDojo
- Check annotation added:
defectdojo.com/product-id
Test with References:
- Create User entity
- Wait for DefectDojo sync
- Create Component with
spec.owner = "user:john.doe" - Verify product_manager set correctly in DefectDojo
Test Updates:
- Update Component name
- Verify Product name updated in DefectDojo
Test Deletes:
- Delete Component
- Verify Product deleted in DefectDojo
4. Error Handling
Check Logs: Plugin operations are logged with tracing:
INFO DefectDojo plugin: Creating DefectDojo product for entity: component-123
INFO DefectDojo plugin: Created DefectDojo product 456 for entity component-123
WARN DefectDojo plugin: Entity component-789 has no DefectDojo product ID, skipping deletion
ERROR DefectDojo plugin: Failed to create product: API error 400: Invalid product type ID
Log Levels:
INFO: Normal operationsWARN: Non-critical issues (missing annotations, skipped operations)ERROR: Critical failures (API errors, invalid configuration)
5. Performance Optimization
Minimize Entity Resolution:
- Cache resolved IDs when possible
- Use static values when appropriate
- Avoid deep resolution chains
Batch Operations:
- Create multiple entities in succession
- Plugin handles each independently
- No manual batching needed
6. Configuration Management
Use Configuration Profiles:
# config.development.toml
[plugins.defectdojo]
base_url = "https://defectdojo-dev.example.com"
auto_create_users = true
# config.production.toml
[plugins.defectdojo]
base_url = "https://defectdojo.example.com"
auto_create_users = false # Require manual user creation
Environment-Specific Settings:
# Development
export CONFIG_FILE="config.development.toml"
# Production
export CONFIG_FILE="config.production.toml"
Troubleshooting
Plugin Not Reacting to Events
Check:
- Plugin enabled:
plugins.defectdojo.enabled = true - Plugin compiled: Run
cargo build - Entity kind matches trigger: Component → product handler
- Event bus running: Check logs for event publications
Field Mapping Not Working
Check:
- Field path correct:
"metadata.name"not"meta.name" - Field exists in entity: Use YAML adapter to inspect entity
- Static value syntax:
{ value = "text" }not"text" - Entity resolution: Resolved entity exists and has required annotation
Entity Resolution Fails
Check:
- Referenced entity exists:
user:john.doeexists in database - Referenced entity has annotation:
defectdojo.com/user-idpresent - Entity kind correct:
resolve_entity = "User"not"user" - Array syntax:
resolve_array = truefor array fields
API Errors
Common Errors:
400 Bad Request: Invalid product type ID
→ Check default_product_type_id or product_type_id mapping
401 Unauthorized: Invalid token
→ Check DEFECTDOJO_API_TOKEN is correct
404 Not Found: Product not found
→ Entity may not have been created yet
409 Conflict: User already exists
→ Normal for users (plugin detects and reuses)
Missing Annotations
If annotations not created:
- Check entity update succeeded:
repository.update()logs - Verify annotation key:
defectdojo.com/product-id - Check database:
SELECT annotations FROM entities WHERE id = '...'
Performance Issues
If plugin causing slowness:
- Check DefectDojo API response times
- Reduce entity resolution depth
- Use static values where possible
- Check network connectivity to DefectDojo
Examples
Example 1: Basic Product Creation
Entity (Component):
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: payment-service
description: Payment processing microservice
tags:
- payment
- critical
- pci-dss
spec:
type: service
lifecycle: production
owner: platform-team
Configuration:
[plugins.defectdojo.field_mappings.product]
name = "metadata.name"
description = "metadata.description"
tags = "metadata.tags"
product_type_id = { value = 1 }
business_criticality = { value = "very high" }
platform = { value = "web" }
lifecycle = "spec.lifecycle"
Result in DefectDojo:
{
"id": 456,
"name": "payment-service",
"description": "Payment processing microservice",
"tags": ["payment", "critical", "pci-dss"],
"prod_type": 1,
"business_criticality": "very high",
"platform": "web",
"lifecycle": "production"
}
Updated Entity:
metadata:
annotations:
defectdojo.com/product-id: "456"
Example 2: Product with User References
Entities:
User:
apiVersion: backstage.io/v1alpha1
kind: User
metadata:
name: john.doe
spec:
profile:
displayName: John Doe
email: john.doe@example.com
memberOf:
- platform-team
Component:
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: payment-service
annotations:
technical-contact: user:jane.smith
spec:
owner: user:john.doe
Configuration:
[plugins.defectdojo.field_mappings.product]
name = "metadata.name"
product_type_id = { value = 1 }
product_manager = {
from = "spec.owner",
resolve_entity = "User",
extract = "annotations.defectdojo.com/user-id"
}
technical_contact = {
from = "metadata.annotations.technical-contact",
resolve_entity = "User",
extract = "annotations.defectdojo.com/user-id"
}
Resolution Process:
- Component created → Product handler triggered
- Field mapper resolves
spec.owner = "user:john.doe" - Queries for User entity with id "john.doe"
- Extracts
annotations.defectdojo.com/user-id = "123" - Sets
product_manager: 123in API call
Example 3: Product Member Assignment
Entities:
Component (already has DD product ID):
metadata:
annotations:
defectdojo.com/product-id: "456"
Users (already have DD user IDs):
# john.doe
metadata:
annotations:
defectdojo.com/user-id: "123"
# jane.smith
metadata:
annotations:
defectdojo.com/user-id: "124"
Group:
apiVersion: backstage.io/v1alpha1
kind: Group
metadata:
name: payment-service-security-team
spec:
type: team
parent: component:payment-service
members:
- user:john.doe
- user:jane.smith
Configuration:
[plugins.defectdojo.field_mappings.product_member]
product_id = {
from = "spec.parent",
resolve_entity = "Component",
extract = "annotations.defectdojo.com/product-id"
}
user_id = {
from = "spec.members",
resolve_entity = "User",
extract = "annotations.defectdojo.com/user-id",
resolve_array = true
}
role_name = { value = "Reader" }
Result:
- Creates Product Member: Product 456 + User 123 + Role "Reader"
- Creates Product Member: Product 456 + User 124 + Role "Reader"
Example 4: Engagement with Full Metadata
Entity:
apiVersion: backstage.io/v1alpha1
kind: Resource
metadata:
name: payment-service-q1-2025-assessment
description: Q1 2025 Security Assessment
annotations:
version: "2.3.0"
commit_hash: "abc123def456"
branch: "release/2.3"
build_id: "build-789"
repo_url: "https://github.com/example/payment-service"
spec:
type: security-assessment
owner: component:payment-service
dependsOn:
- user:security-lead
target_start: "2025-01-01"
target_end: "2025-03-31"
Configuration:
[plugins.defectdojo.field_mappings.engagement]
name = "metadata.name"
description = "metadata.description"
product_id = {
from = "spec.owner",
resolve_entity = "Component",
extract = "annotations.defectdojo.com/product-id"
}
lead_id = {
from = "spec.dependsOn",
resolve_entity = "User",
extract = "annotations.defectdojo.com/user-id"
}
target_start = "spec.target_start"
target_end = "spec.target_end"
version = "metadata.annotations.version"
commit_hash = "metadata.annotations.commit_hash"
branch_tag = "metadata.annotations.branch"
build_id = "metadata.annotations.build_id"
source_code_management_uri = "metadata.annotations.repo_url"
status = { value = "In Progress" }
engagement_type = { value = "CI/CD" }
deduplication_on_engagement = { value = true }
api_test = { value = true }
pen_test = { value = false }
Result in DefectDojo:
{
"id": 789,
"name": "payment-service-q1-2025-assessment",
"description": "Q1 2025 Security Assessment",
"product": 456,
"lead": 123,
"target_start": "2025-01-01",
"target_end": "2025-03-31",
"version": "2.3.0",
"commit_hash": "abc123def456",
"branch_tag": "release/2.3",
"build_id": "build-789",
"source_code_management_uri": "https://github.com/example/payment-service",
"status": "In Progress",
"engagement_type": "CI/CD",
"deduplication_on_engagement": true,
"api_test": true,
"pen_test": false
}
Advanced Topics
Custom Field Mapping Logic
For complex scenarios not covered by built-in mapping types, implement custom resource handlers.
Plugin Development
See docs/PLUGIN_DEVELOPMENT_GUIDE.md (future) for creating new plugins.
Entity Resolution Performance
Entity resolution requires database queries. For high-throughput scenarios:
- Use static values where possible
- Cache resolved IDs in memory
- Consider implementing a resolution cache layer
Multi-Environment Configuration
# Development
cargo run -- --config config.development.toml
# Staging
cargo run -- --config config.staging.toml
# Production
cargo run -- --config config.production.toml
Related Documentation
Support
For issues or questions:
- Check this documentation
- Review plugin logs
- Test with minimal configuration
- Open issue on GitHub with:
- Configuration (sanitized)
- Entity YAML
- Error logs
- Expected vs actual behavior