77from django .contrib .auth .models import Permission
88from django .core .exceptions import ValidationError
99from django .db import IntegrityError
10+ from django .db .models import OuterRef , Value
11+ from django .db .models .functions import Coalesce
1012from django .db .models .query import QuerySet as DjangoQuerySet
1113from django .utils import timezone
1214from django_filters .rest_framework import DjangoFilterBackend
4547from dojo .importers .auto_create_context import AutoCreateContextManager
4648from dojo .jira import services as jira_services
4749from dojo .labels import get_labels
50+ from dojo .location .models import LocationFindingReference , LocationProductReference
51+ from dojo .location .status import FindingLocationStatus
4852from dojo .models import (
4953 App_Analysis ,
5054 Dojo_User ,
6771 get_authorized_languages ,
6872 get_authorized_products ,
6973)
74+ from dojo .query_utils import build_count_subquery
7075from dojo .reports .ui .views import (
7176 prefetch_related_findings_for_report ,
7277 report_url_resolver ,
7378)
7479from dojo .test .queries import get_authorized_tests
80+ from dojo .url .models import URL
7581from dojo .user .utils import get_configuration_permissions_codenames
7682from dojo .utils import (
7783 get_celery_queue_details ,
@@ -261,11 +267,13 @@ def _fetch_and_authorize_parents(self, request, permission_map):
261267 """Fetch parent objects and verify the user has the required permissions."""
262268 data = request .data
263269 parents = {}
264- for field , (model , permission ) in permission_map .items ():
265- obj = model .objects .filter (id = data .get (field )).first ()
266- if obj :
267- user_has_permission_or_403 (request .user , obj , permission )
268- parents [field ] = obj
270+ # TODO: Delete this after the move to Locations
271+ with Endpoint .allow_endpoint_init ():
272+ for field , (model , permission ) in permission_map .items ():
273+ obj = model .objects .filter (id = data .get (field )).first ()
274+ if obj :
275+ user_has_permission_or_403 (request .user , obj , permission )
276+ parents [field ] = obj
269277 return parents
270278
271279 def process_post (self , request ):
@@ -519,6 +527,29 @@ def perform_create(self, serializer):
519527from dojo .notes .api .views import NotesViewSet # noqa: E402, F401 -- re-export; urls.py imports by name
520528
521529
530+ def _report_url_location_refs (product ):
531+ """
532+ URL LocationProductReferences for a product, shaped for V3EndpointCompatibleSerializer.
533+
534+ Mirrors V3EndpointCompatibleViewSet.get_queryset so the report's ``endpoints`` field matches
535+ the V3 ``/endpoints`` route. Non-URL locations (e.g. dependencies) are excluded because the
536+ compat serializer only understands URL-backed locations.
537+ """
538+ active_finding_subquery = build_count_subquery (
539+ LocationFindingReference .objects .filter (
540+ location = OuterRef ("location" ),
541+ status = FindingLocationStatus .Active ,
542+ ),
543+ group_field = "location" ,
544+ )
545+ return LocationProductReference .objects .filter (
546+ product = product ,
547+ location__location_type = URL .LOCATION_TYPE ,
548+ ).annotate (
549+ active_finding_count = Coalesce (active_finding_subquery , Value (0 )),
550+ ).distinct ()
551+
552+
522553def report_generate (request , obj , options ):
523554 user = Dojo_User .objects .get (id = request .user .id )
524555 product_type = None
@@ -589,10 +620,14 @@ def report_generate(request, obj, options):
589620 Finding .objects .filter (test__engagement__product = product ),
590621 ),
591622 )
592- ids = get_endpoint_ids (
593- Endpoint .objects .filter (product = product ).distinct (),
594- )
595- endpoints = Endpoint .objects .filter (id__in = ids )
623+ if settings .V3_FEATURE_LOCATIONS :
624+ endpoints = _report_url_location_refs (product )
625+ else :
626+ # TODO: Delete this after the move to Locations
627+ ids = get_endpoint_ids (
628+ Endpoint .objects .filter (product = product ).distinct (),
629+ )
630+ endpoints = Endpoint .objects .filter (id__in = ids )
596631
597632 elif type (obj ).__name__ == "Engagement" :
598633 engagement = obj
@@ -605,11 +640,14 @@ def report_generate(request, obj, options):
605640 )
606641 report_name = "Engagement Report: " + str (engagement )
607642
608- ids = set (finding .id for finding in findings .qs ) # noqa: C401
609- ids = get_endpoint_ids (
610- Endpoint .objects .filter (product = engagement .product ).distinct (),
611- )
612- endpoints = Endpoint .objects .filter (id__in = ids )
643+ if settings .V3_FEATURE_LOCATIONS :
644+ endpoints = _report_url_location_refs (engagement .product )
645+ else :
646+ # TODO: Delete this after the move to Locations
647+ ids = get_endpoint_ids (
648+ Endpoint .objects .filter (product = engagement .product ).distinct (),
649+ )
650+ endpoints = Endpoint .objects .filter (id__in = ids )
613651
614652 elif type (obj ).__name__ == "Test" :
615653 test = obj
0 commit comments