| 1 | import os |
| 2 | |
| 3 | from fastapi import APIRouter |
| 4 | from fastapi import BackgroundTasks |
| 5 | from fastapi import File |
| 6 | from fastapi import HTTPException |
| 7 | from fastapi import Security |
| 8 | from fastapi import UploadFile |
| 9 | from loguru import logger |
| 10 | |
| 11 | from app.auth.routes.auth import AuthHandler |
| 12 | from app.integrations.scoutsuite.schema.scoutsuite import ( |
| 13 | AvailableScoutSuiteReportsResponse, |
| 14 | ) |
| 15 | from app.integrations.scoutsuite.schema.scoutsuite import AWSScoutSuiteReportRequest |
| 16 | from app.integrations.scoutsuite.schema.scoutsuite import AzureScoutSuiteReportRequest |
| 17 | from app.integrations.scoutsuite.schema.scoutsuite import GCPScoutSuiteReportRequest |
| 18 | from app.integrations.scoutsuite.schema.scoutsuite import ScoutSuiteReportOptions |
| 19 | from app.integrations.scoutsuite.schema.scoutsuite import ( |
| 20 | ScoutSuiteReportOptionsResponse, |
| 21 | ) |
| 22 | from app.integrations.scoutsuite.schema.scoutsuite import ScoutSuiteReportResponse |
| 23 | from app.integrations.scoutsuite.services.scoutsuite import ( |
| 24 | generate_aws_report_background, |
| 25 | ) |
| 26 | from app.integrations.scoutsuite.services.scoutsuite import ( |
| 27 | generate_azure_report_background, |
| 28 | ) |
| 29 | from app.integrations.scoutsuite.services.scoutsuite import ( |
| 30 | generate_gcp_report_background, |
| 31 | ) |
| 32 | from app.integrations.scoutsuite.services.scoutsuite import read_json_file |
| 33 | from app.integrations.scoutsuite.services.scoutsuite import save_file_to_directory |
| 34 | from app.integrations.scoutsuite.services.scoutsuite import validate_json_data |
| 35 | |
| 36 | integration_scoutsuite_router = APIRouter() |
| 37 | |
| 38 | |
| 39 | @integration_scoutsuite_router.get( |
| 40 | "/report-generation-options", |
| 41 | response_model=ScoutSuiteReportOptionsResponse, |
| 42 | description="Get the available report generation options.", |
| 43 | dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))], |
| 44 | ) |
| 45 | async def get_report_generation_options(): |
| 46 | """ |
| 47 | Retrieves the available report generation options for ScoutSuite. |
| 48 | |
| 49 | Returns: |
| 50 | ScoutSuiteReportOptionsResponse: The response containing the available report generation options. |
| 51 | """ |
| 52 | return ScoutSuiteReportOptionsResponse( |
| 53 | options=[ScoutSuiteReportOptions.aws, ScoutSuiteReportOptions.azure, ScoutSuiteReportOptions.gcp], |
| 54 | success=True, |
| 55 | message="ScoutSuite Report generation options retrieved successfully", |
| 56 | ) |
| 57 | |
| 58 | |
| 59 | @integration_scoutsuite_router.get( |
| 60 | "/available-reports", |
| 61 | response_model=AvailableScoutSuiteReportsResponse, |
| 62 | description="Get the available ScoutSuite reports.", |
| 63 | dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))], |
| 64 | ) |
| 65 | async def get_available_reports(): |
| 66 | """ |
| 67 | List all the `.html` files from the `scoutsuite-report` directory |
| 68 | |
| 69 | Returns: |
| 70 | AvailableScoutSuiteReportsResponse: The response containing the list of available ScoutSuite reports. |
| 71 | Raises: |
| 72 | HTTPException: If the directory does not exist. |
| 73 | """ |
| 74 | directory = "scoutsuite-report" |
| 75 | full_path = os.path.abspath(directory) |
| 76 | |
| 77 | logger.info(f"Checking directory: {full_path}") |
| 78 | |
| 79 | if not os.path.exists(directory): |
| 80 | raise HTTPException(status_code=404, detail="Directory does not exist") |
| 81 | |
| 82 | files = os.listdir(directory) |
| 83 | html_files = [file for file in files if file.endswith(".html")] |
| 84 | |
| 85 | return AvailableScoutSuiteReportsResponse( |
| 86 | available_reports=html_files, |
| 87 | success=True, |
| 88 | message="Available ScoutSuite reports retrieved successfully", |
| 89 | ) |
| 90 | |
| 91 | |
| 92 | @integration_scoutsuite_router.post( |
| 93 | "/generate-aws-report", |
| 94 | response_model=ScoutSuiteReportResponse, |
| 95 | dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))], |
| 96 | ) |
| 97 | async def generate_aws_report( |
| 98 | background_tasks: BackgroundTasks, |
| 99 | request: AWSScoutSuiteReportRequest, |
| 100 | ): |
| 101 | """ |
| 102 | Endpoint to generate an AWS ScoutSuite report. |
| 103 | |
| 104 | Args: |
| 105 | background_tasks (BackgroundTasks): The background tasks object. |
| 106 | request (AWSScoutSuiteReportRequest): The request object. |
| 107 | session (AsyncSession): The async session object for database operations. |
| 108 | """ |
| 109 | background_tasks.add_task(generate_aws_report_background, request) |
| 110 | return ScoutSuiteReportResponse( |
| 111 | success=True, |
| 112 | message="AWS ScoutSuite report generation started successfully. This will take a few minutes to complete. Check back in shortly.", |
| 113 | ) |
| 114 | |
| 115 | |
| 116 | @integration_scoutsuite_router.post( |
| 117 | "/generate-azure-report", |
| 118 | response_model=ScoutSuiteReportResponse, |
| 119 | dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))], |
| 120 | ) |
| 121 | async def generate_azure_report( |
| 122 | background_tasks: BackgroundTasks, |
| 123 | request: AzureScoutSuiteReportRequest, |
| 124 | ): |
| 125 | """ |
| 126 | Endpoint to generate an Azure ScoutSuite report. |
| 127 | |
| 128 | Args: |
| 129 | background_tasks (BackgroundTasks): The background tasks object. |
| 130 | request (AzureScoutSuiteReportRequest): The request object. |
| 131 | session (AsyncSession): The async session object for database operations. |
| 132 | """ |
| 133 | background_tasks.add_task(generate_azure_report_background, request) |
| 134 | return ScoutSuiteReportResponse( |
| 135 | success=True, |
| 136 | message="Azure ScoutSuite report generation started successfully. This will take a few minutes to complete. Check back in shortly.", |
| 137 | ) |
| 138 | |
| 139 | |
| 140 | @integration_scoutsuite_router.post( |
| 141 | "/generate-gcp-report", |
| 142 | response_model=ScoutSuiteReportResponse, |
| 143 | dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))], |
| 144 | ) |
| 145 | async def generate_gcp_report( |
| 146 | background_tasks: BackgroundTasks, |
| 147 | file: UploadFile = File(...), |
| 148 | report_name: str = "gcp-report", |
| 149 | ): |
| 150 | """ |
| 151 | Endpoint to generate a GCP ScoutSuite report. |
| 152 | |
| 153 | Args: |
| 154 | background_tasks (BackgroundTasks): The background tasks object. |
| 155 | file (UploadFile): The uploaded JSON file. |
| 156 | """ |
| 157 | # Read the file contents |
| 158 | contents = await file.read() |
| 159 | |
| 160 | # Read and validate the JSON file |
| 161 | data = await read_json_file(contents) |
| 162 | validate_json_data(data) |
| 163 | |
| 164 | # Save the file to the scoutsuite-report directory |
| 165 | directory = os.path.join(os.getcwd(), "scoutsuite-report") |
| 166 | file_path = await save_file_to_directory(contents, directory, file.filename) |
| 167 | |
| 168 | logger.info(f"File saved to: {file_path}") |
| 169 | request = GCPScoutSuiteReportRequest(report_name=report_name, file_path=file_path) |
| 170 | logger.info(f"Request: {request}") |
| 171 | background_tasks.add_task(generate_gcp_report_background, request) |
| 172 | return ScoutSuiteReportResponse( |
| 173 | success=True, |
| 174 | message="GCP ScoutSuite report generation started successfully. This will take a few minutes to complete. Check back in shortly.", |
| 175 | ) |
| 176 | |
| 177 | |
| 178 | @integration_scoutsuite_router.delete( |
| 179 | "/delete-report/{report_name}", |
| 180 | response_model=ScoutSuiteReportResponse, |
| 181 | dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))], |
| 182 | ) |
| 183 | async def delete_report( |
| 184 | report_name: str, |
| 185 | ): |
| 186 | """ |
| 187 | Endpoint to delete a ScoutSuite report. |
| 188 | |
| 189 | Args: |
| 190 | report_name (str): The name of the report to delete. |
| 191 | """ |
| 192 | report_base_name = os.path.splitext(report_name)[0] |
| 193 | report_file_path = f"scoutsuite-report/{report_name}" |
| 194 | exceptions_file_path = f"scoutsuite-report/scoutsuite-results/scoutsuite_exceptions_{report_base_name}.js" |
| 195 | results_file_path = f"scoutsuite-report/scoutsuite-results/scoutsuite_results_{report_base_name}.js" |
| 196 | |
| 197 | files_to_delete = [report_file_path, exceptions_file_path, results_file_path] |
| 198 | |
| 199 | for file_path in files_to_delete: |
| 200 | if os.path.exists(file_path): |
| 201 | os.remove(file_path) |
| 202 | |
| 203 | return ScoutSuiteReportResponse(success=True, message=f"Report {report_name} and associated files deleted successfully") |