-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Add Simple Metrics API endpoint #12533
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Conversation
🔴 Risk threshold exceeded.This pull request introduces several security concerns, including potential information disclosure through error messages and metrics endpoints, user enumeration via logout logging, and a possible denial of service vulnerability in the metrics view. The changes affect multiple files in the dojo directory and require careful review to mitigate potential security risks.
|
Vulnerability | Configured Codepaths Edit |
---|---|
Description | Sensitive edits detected for this file. Sensitive file paths and allowed authors can be configured in .dryrunsecurity.yaml . |
⚠️ Configured Codepaths Edit in dojo/utils.py
Vulnerability | Configured Codepaths Edit |
---|---|
Description | Sensitive edits detected for this file. Sensitive file paths and allowed authors can be configured in .dryrunsecurity.yaml . |
⚠️ Configured Codepaths Edit in dojo/api_v2/serializers.py
Vulnerability | Configured Codepaths Edit |
---|---|
Description | Sensitive edits detected for this file. Sensitive file paths and allowed authors can be configured in .dryrunsecurity.yaml . |
⚠️ Configured Codepaths Edit in dojo/api_v2/views.py
Vulnerability | Configured Codepaths Edit |
---|---|
Description | Sensitive edits detected for this file. Sensitive file paths and allowed authors can be configured in .dryrunsecurity.yaml . |
⚠️ Information Disclosure via Error Messages in docs/content/en/api/metrics-endpoint.md
Vulnerability | Information Disclosure via Error Messages |
---|---|
Description | The documentation provides detailed error messages that could help an attacker understand backend validation logic. While informative for developers, these specific error messages reveal implementation details that could aid in probing the API's input validation mechanisms. |
django-DefectDojo/docs/content/en/api/metrics-endpoint.md
Lines 1 to 169 in 1737ad2
--- | |
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, | |
"S0": 5, // Critical | |
"S1": 25, // High | |
"S2": 75, // Medium | |
"S3": 40, // Low | |
"S4": 5, // Info | |
"Opened": 10, | |
"Closed": 8 | |
}, | |
{ | |
"product_type_id": 2, | |
"product_type_name": "Mobile Application", | |
"Total": 89, | |
"S0": 2, // Critical | |
"S1": 15, // High | |
"S2": 45, // Medium | |
"S3": 25, // Low | |
"S4": 2, // Info | |
"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 | | |
| `S0` | Integer | Number of Critical severity findings | | |
| `S1` | Integer | Number of High severity findings | | |
| `S2` | Integer | Number of Medium severity findings | | |
| `S3` | Integer | Number of Low severity findings | | |
| `S4` | 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 specific field names (`S0`, `S1`, `S2`, `S3`, `S4` for severity levels and `Total`, `Opened`, `Closed` for counts) to maintain consistency with the internal data structure | |
- **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 |
⚠️ User Enumeration via Logout Logging in dojo/utils.py
Vulnerability | User Enumeration via Logout Logging |
---|---|
Description | The logout logging differentiates between authenticated and anonymous users, which could potentially provide a side channel for user enumeration if log entries are accessible or observable. |
django-DefectDojo/dojo/utils.py
Lines 2387 to 2396 in 1737ad2
@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) |
⚠️ Information Disclosure via New API Endpoint in dojo/urls.py
Vulnerability | Information Disclosure via New API Endpoint |
---|---|
Description | The new metrics endpoint could expose sensitive system information if not properly secured. The introduction of this endpoint requires careful review to ensure it does not leak operational details. |
django-DefectDojo/dojo/urls.py
Lines 58 to 64 in 1737ad2
ReImportScanView, | |
RiskAcceptanceViewSet, | |
RoleViewSet, | |
SimpleMetricsViewSet, | |
SLAConfigurationViewset, | |
SonarqubeIssueTransitionViewSet, | |
SonarqubeIssueViewSet, |
⚠️ Information Disclosure via Metrics in dojo/api_v2/serializers.py
Vulnerability | Information Disclosure via Metrics |
---|---|
Description | The serializer exposes detailed metrics about product types, including vulnerability counts by severity. If not properly protected, this could provide an attacker insights into the organization's security posture and vulnerability landscape. |
django-DefectDojo/dojo/api_v2/serializers.py
Lines 3089 to 3107 in 1737ad2
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) | |
S0 = serializers.IntegerField(read_only=True) # Critical | |
S1 = serializers.IntegerField(read_only=True) # High | |
S2 = serializers.IntegerField(read_only=True) # Medium | |
S3 = serializers.IntegerField(read_only=True) # Low | |
S4 = serializers.IntegerField(read_only=True) # Info | |
Opened = serializers.IntegerField(read_only=True) | |
Closed = serializers.IntegerField(read_only=True) |
⚠️ Potential Denial of Service in dojo/api_v2/views.py
Vulnerability | Potential Denial of Service |
---|---|
Description | The SimpleMetricsViewSet sets pagination_class = None , which could allow resource exhaustion if a large number of product types or findings are processed. This could lead to excessive server resource consumption. |
django-DefectDojo/dojo/api_v2/views.py
Lines 11 to 17 in 1737ad2
from django.contrib.auth.models import Permission | |
from django.core.exceptions import ValidationError | |
from django.db import IntegrityError | |
from django.db.models import Count, Q | |
from django.http import FileResponse, Http404, HttpResponse | |
from django.shortcuts import get_object_or_404 | |
from django.utils import timezone |
⚠️ Information Disclosure via Error Messaging in dojo/api_v2/views.py
Vulnerability | Information Disclosure via Error Messaging |
---|---|
Description | The error message for product_type_id does not clearly distinguish between a non-existent product type and an unauthorized product type. This could potentially allow for subtle user enumeration. |
django-DefectDojo/dojo/api_v2/views.py
Lines 11 to 17 in 1737ad2
from django.contrib.auth.models import Permission | |
from django.core.exceptions import ValidationError | |
from django.db import IntegrityError | |
from django.db.models import Count, Q | |
from django.http import FileResponse, Http404, HttpResponse | |
from django.shortcuts import get_object_or_404 | |
from django.utils import timezone |
We've notified @mtesauro.
All finding details can be found in the DryRun Security Dashboard.
S0 = serializers.IntegerField(read_only=True) # Critical | ||
S1 = serializers.IntegerField(read_only=True) # High | ||
S2 = serializers.IntegerField(read_only=True) # Medium | ||
S3 = serializers.IntegerField(read_only=True) # Low | ||
S4 = serializers.IntegerField(read_only=True) # Info |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just name these after the severity level rather than commenting and documenting the conversion for Sx -> severity level?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The S0-S4 naming is maintained for potential backward compatibility and codebase consistency. The Sx pattern is used consistently throughout the UI's templates and database queries. That said I am happy to make the change to named labels to make it more clear for anyone integrating.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, the S0-4 naming is the absolute definition of legacy code. That was initially used in the very, very early versions of DefectDojo then transitions to the more typical "Critical, High, ..."
So, even though the code has S# all over it, the 'modern' version of that is the more human friendly labels.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The (almost the) same simple metrics queries are also in dojo/metrics/views.py
. Could you look at having them in once place?
Description
This PR adds a REST API endpoint
/api/v2/metrics/simple
for programmatic access to DefectDojo's simple metrics functionality. This will enable automated reporting and external dashboard integrationChanges:
/metrics/simple
endpointdojo/utils.py
that caused crashes when anonymous users triggered logout events by handling null user casesTest results
uwsgi-1 | ----------------------------------------------------------------------
uwsgi-1 | Ran 43 tests in 4.588s
uwsgi-1 |
uwsgi-1 | OK (skipped=32)
Documentation
Checklist
dev
.dev
.bugfix
branch.Response