88from django .core .exceptions import ValidationError
99from django .db import IntegrityError
1010from django .db .models .query import QuerySet as DjangoQuerySet
11+ from django .shortcuts import render
1112from django .utils import timezone
1213from django_filters .rest_framework import DjangoFilterBackend
1314from drf_spectacular .renderers import OpenApiJsonRenderer2
3536from dojo .authorization import api_permissions as permissions
3637from dojo .authorization .authorization import user_has_permission_or_403
3738from dojo .endpoint .ui .views import get_endpoint_ids
39+ from dojo .engagement .queries import get_authorized_engagements
3840from dojo .filters import (
3941 ApiAppAnalysisFilter ,
4042 ApiDojoMetaFilter ,
4143)
44+ from dojo .finding .queries import get_authorized_findings
4245from dojo .finding .ui .filters import (
4346 ReportFindingFilter ,
4447 ReportFindingFilterWithoutObjectLookups ,
6972 get_authorized_products ,
7073)
7174from dojo .reports .ui .views import (
75+ CSVExportView ,
76+ ExcelExportView ,
7277 prefetch_related_findings_for_report ,
7378 report_url_resolver ,
7479)
@@ -525,8 +530,8 @@ def perform_create(self, serializer):
525530 pghistory .context (test_id = test_id_from_response )
526531
527532
528- from dojo .note_type .api .views import NoteTypeViewSet # noqa: E402, F401 -- re-export; urls.py imports by name
529- from dojo .notes .api .views import NotesViewSet # noqa: E402, F401 -- re-export; urls.py imports by name
533+ from dojo .note_type .api .views import NoteTypeViewSet # noqa: E402, F401
534+ from dojo .notes .api .views import NotesViewSet # noqa: E402, F401
530535
531536
532537def report_generate (request , obj , options ):
@@ -537,6 +542,9 @@ def report_generate(request, obj, options):
537542 test = None
538543 endpoint = None
539544 endpoints = None
545+ report_context = {}
546+ report_template = None
547+ report_title = "Generate Report"
540548
541549 include_finding_notes = False
542550 include_finding_images = False
@@ -560,14 +568,16 @@ def report_generate(request, obj, options):
560568
561569 if type (obj ).__name__ == "Product_Type" :
562570 product_type = obj
571+ report_template = "dojo/product_type_pdf_report.html"
572+ report_title = labels .ORG_REPORT_LABEL
563573
564574 report_name = labels .ORG_REPORT_WITH_NAME_TITLE % {"name" : str (product_type )}
565575
566576 findings = report_finding_filter_class (
567577 request .GET ,
568578 prod_type = product_type ,
569579 queryset = prefetch_related_findings_for_report (
570- Finding . objects .filter (
580+ get_authorized_findings ( "view" ) .filter (
571581 test__engagement__product__prod_type = product_type ,
572582 ),
573583 ),
@@ -586,9 +596,26 @@ def report_generate(request, obj, options):
586596 months_between = (r .years * 12 ) + r .months
587597 # include current month
588598 months_between += 1
599+ report_context = {
600+ "products" : get_authorized_products ("view" ).filter (
601+ prod_type = product_type ,
602+ engagement__test__finding__in = findings .qs ,
603+ ).distinct (),
604+ "engagements" : get_authorized_engagements ("view" ).filter (
605+ product__prod_type = product_type ,
606+ test__finding__in = findings .qs ,
607+ ).distinct (),
608+ "tests" : get_authorized_tests ("view" ).filter (
609+ engagement__product__prod_type = product_type ,
610+ finding__in = findings .qs ,
611+ ).distinct (),
612+ "months_between" : months_between ,
613+ }
589614
590615 elif type (obj ).__name__ == "Product" :
591616 product = obj
617+ report_template = "dojo/product_pdf_report.html"
618+ report_title = labels .ASSET_REPORT_LABEL
592619
593620 report_name = labels .ASSET_REPORT_WITH_NAME_TITLE % {"name" : str (product )}
594621
@@ -606,6 +633,8 @@ def report_generate(request, obj, options):
606633
607634 elif type (obj ).__name__ == "Engagement" :
608635 engagement = obj
636+ report_template = "dojo/engagement_pdf_report.html"
637+ report_title = "Engagement Report"
609638 findings = report_finding_filter_class (
610639 request .GET ,
611640 engagement = engagement ,
@@ -623,6 +652,8 @@ def report_generate(request, obj, options):
623652
624653 elif type (obj ).__name__ == "Test" :
625654 test = obj
655+ report_template = "dojo/test_pdf_report.html"
656+ report_title = "Test Report"
626657 findings = report_finding_filter_class (
627658 request .GET ,
628659 engagement = test .engagement ,
@@ -634,6 +665,8 @@ def report_generate(request, obj, options):
634665
635666 elif type (obj ).__name__ == "Endpoint" :
636667 endpoint = obj
668+ report_template = "dojo/endpoint_pdf_report.html"
669+ report_title = "Endpoint Report"
637670 host = endpoint .host
638671 report_name = "Endpoint Report: " + host
639672 endpoints = Endpoint .objects .filter (
@@ -648,6 +681,8 @@ def report_generate(request, obj, options):
648681
649682 elif isinstance (obj , DjangoQuerySet ):
650683 # Support any Django QuerySet (including Tagulous CastTaggedQuerySet)
684+ report_template = "dojo/finding_pdf_report.html"
685+ report_title = "Finding Report"
651686 findings = report_finding_filter_class (
652687 request .GET ,
653688 queryset = prefetch_related_findings_for_report (obj ).distinct (),
@@ -670,12 +705,20 @@ def report_generate(request, obj, options):
670705 "endpoint" : endpoint ,
671706 "endpoints" : endpoints ,
672707 "findings" : findings .qs .order_by ("numerical_severity" ),
708+ "include_finding_notes" : include_finding_notes ,
709+ "include_finding_images" : include_finding_images ,
710+ "include_executive_summary" : include_executive_summary ,
673711 "include_table_of_contents" : include_table_of_contents ,
712+ "include_disclaimer" : get_system_setting ("disclaimer_reports_forced" , 0 ),
713+ "disclaimer" : get_system_setting ("disclaimer_reports" ),
674714 "user" : user ,
675715 "team_name" : settings .TEAM_NAME ,
676- "title" : "Generate Report" ,
716+ "title" : report_title ,
677717 "user_id" : request .user .id ,
678718 "host" : report_url_resolver (request ),
719+ "host_view" : False ,
720+ "context" : report_context ,
721+ "report_template" : report_template ,
679722 }
680723
681724 finding_notes = []
@@ -792,6 +835,43 @@ def report_generate(request, obj, options):
792835 return result
793836
794837
838+ def _report_findings_filename (obj , extension ):
839+ object_id = getattr (obj , "id" , None )
840+ if object_id is None :
841+ return f"findings.{ extension } "
842+
843+ object_type = type (obj ).__name__ .lower ()
844+ return f"{ object_type } _{ object_id } _findings.{ extension } "
845+
846+
847+ def report_generate_response (request , obj , options ):
848+ report_type = options .get ("report_type" , "JSON" )
849+
850+ if report_type not in {"CSV" , "Excel" , "HTML" , "JSON" }:
851+ msg = f"Unsupported report_type: { report_type } "
852+ raise ValidationError (msg )
853+
854+ data = report_generate (request , obj , options )
855+
856+ if report_type == "JSON" :
857+ report = serializers .ReportGenerateSerializer (data )
858+ return Response (report .data )
859+
860+ if report_type == "HTML" :
861+ return render (request , data ["report_template" ], data )
862+
863+ if report_type == "CSV" :
864+ return CSVExportView ().build_response (
865+ data ["findings" ],
866+ filename = _report_findings_filename (obj , "csv" ),
867+ )
868+
869+ return ExcelExportView ().build_response (
870+ data ["findings" ],
871+ filename = _report_findings_filename (obj , "xlsx" ),
872+ )
873+
874+
795875class CeleryViewSet (viewsets .ViewSet ):
796876 permission_classes = (permissions .IsSuperUser , DjangoModelPermissions )
797877 queryset = System_Settings .objects .none ()
0 commit comments