Skip to content

Add Simple Metrics API endpoint

405b676
Select commit
Loading
Failed to load commit list.
Closed

Add Simple Metrics API endpoint #12533

Add Simple Metrics API endpoint
405b676
Select commit
Loading
Failed to load commit list.
DryRunSecurity / General Security Analyzer succeeded Jun 19, 2025 in 40s

DryRun Security

Details

General Security Analyzer Findings: 5 detected

⚠️ Metrics Information Disclosure dojo/api_v2/serializers.py (click for details)
Type Metrics Information Disclosure
Description The new SimpleMetricsSerializer exposes detailed security metrics, including finding counts by severity and status. If not properly secured, this could provide attackers with insights into the application's security posture and internal metrics.
Filename dojo/api_v2/serializers.py
CodeLink
class Meta:
model = Notification_Webhooks
fields = "__all__"
class SimpleMetricsSerializer(serializers.Serializer):
"""Serializer for simple metrics data grouped by product type."""
product_type_id = serializers.IntegerField(read_only=True)
product_type_name = serializers.CharField(read_only=True)
Total = serializers.IntegerField(read_only=True)
# Severity labels
critical = serializers.IntegerField(read_only=True)
high = serializers.IntegerField(read_only=True)
medium = serializers.IntegerField(read_only=True)
low = serializers.IntegerField(read_only=True)
info = serializers.IntegerField(read_only=True)
Opened = serializers.IntegerField(read_only=True)
Closed = serializers.IntegerField(read_only=True)
⚠️ Logout IP Logging dojo/utils.py (click for details)
Type Logout IP Logging
Description The patch introduces logging of IP addresses for both authenticated and anonymous logout attempts. While logging can be useful for auditing, logging IP addresses for anonymous users could potentially be used for tracking or reconnaissance if not carefully managed.
Filename dojo/utils.py
CodeLink
@receiver(user_logged_out)
def log_user_logout(sender, request, user, **kwargs):
if user:
logger.info("logout user: %s via ip: %s", user.username, request.META.get("REMOTE_ADDR"))
else:
logger.info("logout attempt for anonymous user via ip: %s", request.META.get("REMOTE_ADDR"))
@receiver(user_login_failed)
⚠️ Product Type Enumeration dojo/api_v2/views.py (click for details)
Type Product Type Enumeration
Description The new metrics API endpoint allows filtering by product type ID, which could potentially enable an attacker to enumerate valid product type IDs through careful probing of the API responses. While the implementation includes authorization checks, the distinct error messages and response handling could leak information about the existence of product types.
Filename dojo/api_v2/views.py
CodeLink
return Answered_Survey.objects.all().order_by("id")
# Authorization: authenticated
class SimpleMetricsViewSet(
viewsets.ReadOnlyModelViewSet,
):
"""
Simple metrics API endpoint that provides finding counts by product type
broken down by severity and month status.
"""
serializer_class = serializers.SimpleMetricsSerializer
queryset = Product_Type.objects.none()
permission_classes = (IsAuthenticated,)
pagination_class = None
@extend_schema(
parameters=[
OpenApiParameter(
"date",
OpenApiTypes.DATE,
OpenApiParameter.QUERY,
required=False,
description="Date to generate metrics for (YYYY-MM-DD format). Defaults to current month.",
),
OpenApiParameter(
"product_type_id",
OpenApiTypes.INT,
OpenApiParameter.QUERY,
required=False,
description="Optional product type ID to filter metrics. If not provided, returns all accessible product types.",
),
],
responses={status.HTTP_200_OK: serializers.SimpleMetricsSerializer(many=True)},
)
def list(self, request):
"""Retrieve simple metrics data for the requested month grouped by product type."""
from dojo.metrics.views import get_simple_metrics_data
# Parse the date parameter, default to current month
now = timezone.now()
date_param = request.query_params.get("date")
product_type_id = request.query_params.get("product_type_id")
if date_param:
# Input validation
if len(date_param) > 20:
return Response(
{"error": "Invalid date parameter length."},
status=status.HTTP_400_BAD_REQUEST,
)
# Sanitize input - only allow alphanumeric characters and hyphens
import re
if not re.match(r"^[0-9\-]+$", date_param):
return Response(
{"error": "Invalid date format. Only numbers and hyphens allowed."},
status=status.HTTP_400_BAD_REQUEST,
)
try:
# Parse date string with validation
parsed_date = datetime.strptime(date_param, "%Y-%m-%d")
# Date range validation
min_date = datetime(2000, 1, 1)
max_date = datetime.now() + relativedelta(years=1)
if parsed_date < min_date or parsed_date > max_date:
return Response(
{"error": "Date must be between 2000-01-01 and one year from now."},
status=status.HTTP_400_BAD_REQUEST,
)
# Make it timezone aware
now = timezone.make_aware(parsed_date) if timezone.is_naive(parsed_date) else parsed_date
except ValueError:
return Response(
{"error": "Invalid date format. Use YYYY-MM-DD format."},
status=status.HTTP_400_BAD_REQUEST,
)
# Optional filtering by specific product type with validation
parsed_product_type_id = None
if product_type_id:
try:
parsed_product_type_id = int(product_type_id)
except ValueError:
return Response(
{"error": "Invalid product_type_id format."},
status=status.HTTP_400_BAD_REQUEST,
)
# Get metrics data
try:
metrics_data = get_simple_metrics_data(
now,
parsed_product_type_id,
)
except Exception as e:
logger.error(f"Error retrieving metrics: {e}")
return Response(
{"error": "Unable to retrieve metrics data."},
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
)
# Check if product type was requested but not found
if parsed_product_type_id and not metrics_data:
return Response(
{"error": "Product type not found or access denied."},
status=status.HTTP_404_NOT_FOUND,
)
serializer = self.serializer_class(metrics_data, many=True)
return Response(serializer.data)
# Authorization: configuration
class AnnouncementViewSet(
DojoModelViewSet,
⚠️ Test Credential Linting Disabled ruff.toml (click for details)
Type Test Credential Linting Disabled
Description Disabling the S106 linting rule for test files removes warnings about hardcoded passwords, which could lead to accidental exposure of credentials if test files are mishandled or the repository is compromised.
Filename ruff.toml
CodeLink

django-DefectDojo/ruff.toml

Lines 112 to 118 in 405b676

[lint.per-file-ignores]
"unittests/**" = [
"S105", # hardcoded passwords in tests are fine
"S106", # hardcoded password assigned to argument in tests are fine
"S108", # tmp paths mentioned in tests are fine
]
⚠️ Verbose Error Message Disclosure docs/content/en/api/metrics-endpoint.md (click for details)
Type Verbose Error Message Disclosure
Description The API documentation reveals detailed error messages that provide insights into the backend's validation logic. While informative for developers, these messages could assist an attacker in understanding the application's input validation mechanisms and potential attack surfaces.
Filename docs/content/en/api/metrics-endpoint.md
CodeLink
---
title: "Simple Metrics API Endpoint"
description: "API endpoint for retrieving finding metrics by product type with severity breakdown"
draft: false
weight: 3
---
## Simple Metrics API Endpoint
The Simple Metrics API endpoint provides finding counts by product type, broken down by severity levels and month status. This endpoint replicates the data from the UI's `/metrics/simple` page in JSON format, making it easier to integrate with other tools and dashboards.
### Endpoint Details
**URL:** `/api/v2/metrics/simple`
**Method:** `GET`
**Authentication:** Required (Token authentication)
**Authorization:** User must have `Product_Type_View` permission for the product types
### Query Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `date` | String (YYYY-MM-DD) | No | Date to filter metrics by month/year (defaults to current month) |
| `product_type_id` | Integer | No | Optional product type ID to filter metrics. If not provided, returns all accessible product types |
### Response Format
The endpoint returns an array of objects, each representing metrics for a product type:
```json
[
{
"product_type_id": 1,
"product_type_name": "Web Application",
"Total": 150,
"critical": 5,
"high": 25,
"medium": 75,
"low": 40,
"info": 5,
"Opened": 10,
"Closed": 8
},
{
"product_type_id": 2,
"product_type_name": "Mobile Application",
"Total": 89,
"critical": 2,
"high": 15,
"medium": 45,
"low": 25,
"info": 2,
"Opened": 7,
"Closed": 5
}
]
```
### Response Fields
| Field | Type | Description |
|-------|------|-------------|
| `product_type_id` | Integer | Unique identifier for the product type |
| `product_type_name` | String | Name of the product type |
| `Total` | Integer | Total number of findings for the product type in the specified month |
| `critical` | Integer | Number of Critical severity findings |
| `high` | Integer | Number of High severity findings |
| `medium` | Integer | Number of Medium severity findings |
| `low` | Integer | Number of Low severity findings |
| `info` | Integer | Number of Info severity findings |
| `Opened` | Integer | Number of findings opened in the specified month |
| `Closed` | Integer | Number of findings closed in the specified month |
### Example Usage
#### Get current month metrics
```bash
GET /api/v2/metrics/simple
```
#### Get metrics for January 2024
```bash
GET /api/v2/metrics/simple?date=2024-01-15
```
#### Get metrics for a specific product type
```bash
GET /api/v2/metrics/simple?product_type_id=1
```
#### Get metrics for a specific product type and date
```bash
GET /api/v2/metrics/simple?date=2024-05-01&product_type_id=2
```
### Error Responses
#### 400 Bad Request - Invalid date characters
```json
{
"error": "Invalid date format. Only numbers and hyphens allowed."
}
```
#### 400 Bad Request - Invalid date format
```json
{
"error": "Invalid date format. Use YYYY-MM-DD format."
}
```
#### 400 Bad Request - Date out of range
```json
{
"error": "Date must be between 2000-01-01 and one year from now."
}
```
#### 400 Bad Request - Invalid product_type_id format
```json
{
"error": "Invalid product_type_id format."
}
```
#### 404 Not Found - Product type not found or access denied
```json
{
"error": "Product type not found or access denied."
}
```
#### 403 Unauthorized - Missing or invalid authentication
```json
{
"detail": "Authentication credentials were not provided."
}
```
#### 403 Forbidden - Insufficient permissions
```json
{
"detail": "You do not have permission to perform this action."
}
```
### Notes
- **Authorization Model**: This endpoint uses the same authorization model as the UI's `/metrics/simple` page, ensuring consistent access control
- **Performance**: The endpoint is optimized with database aggregation instead of Python loops for better performance
- **Date Handling**: If no date is provided, the current month is used by default
- **Timezone**: All dates are handled in the server's configured timezone
- **Product Type Access**: Users will only see metrics for product types they have permission to view
- **Data Consistency**: The data returned by this API endpoint matches exactly what is displayed on the `/metrics/simple` UI page
- **Field Naming**: The API uses descriptive field names (`critical`, `high`, `medium`, `low`, `info` for severity levels and `Total`, `Opened`, `Closed` for counts) to maintain consistency and readability
- **URL Format**: The endpoint automatically redirects requests without trailing slash to include one (301 redirect)
- **Date Validation**: The API performs two levels of date validation: first checking for valid characters (only numbers and hyphens allowed), then validating the YYYY-MM-DD format
### Use Cases
This endpoint is useful for:
- **Dashboard Integration**: Integrating DefectDojo metrics into external dashboards and reporting tools
- **Automated Reporting**: Creating automated reports showing security metrics trends over time
- **CI/CD Integration**: Monitoring security metrics as part of continuous integration pipelines
- **Executive Reporting**: Generating high-level security metrics for management reporting
- **Data Analysis**: Performing custom analysis on security finding trends and patterns