diff --git a/backend/app/agents/routes/agents.py b/backend/app/agents/routes/agents.py index a3d370d3..90e4fc94 100644 --- a/backend/app/agents/routes/agents.py +++ b/backend/app/agents/routes/agents.py @@ -16,6 +16,8 @@ from sqlalchemy.future import select from app.agents.schema.agents import AgentModifyResponse +from app.threat_intel.services.epss import collect_epss_score +from app.threat_intel.schema.epss import EpssThreatIntelRequest from app.agents.schema.agents import AgentsResponse from app.incidents.services.db_operations import list_cases_by_asset_name from app.incidents.schema.db_operations import CaseOutResponse @@ -464,11 +466,11 @@ async def get_agent_vulnerabilities( return await collect_agent_vulnerabilities(agent_id, vulnerability_severity.value) @agents_router.get( - "/{agent_id}/csv/vulnerabilities", + "/{agent_id}/csv/vulnerabilities/{vulnerability_severity}", description="Get agent vulnerabilities as CSV", dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))], ) -async def get_agent_vulnerabilities_csv(agent_id: str, session: AsyncSession = Depends(get_db)): +async def get_agent_vulnerabilities_csv(agent_id: str, vulnerability_severity: VulnSeverity = Path(...)) -> StreamingResponse: """ Fetches the vulnerabilities of a specific agent and returns them as a CSV file. @@ -482,9 +484,9 @@ async def get_agent_vulnerabilities_csv(agent_id: str, session: AsyncSession = D wazuh_new = await check_wazuh_manager_version() if wazuh_new is True: logger.info("Wazuh Manager version is 4.8.0 or higher. Fetching vulnerabilities using new API") - vulnerabilities = (await collect_agent_vulnerabilities_new(agent_id, vulnerability_severity="High")).vulnerabilities + vulnerabilities = (await collect_agent_vulnerabilities_new(agent_id, vulnerability_severity=vulnerability_severity.value)).vulnerabilities else: - vulnerabilities = await collect_agent_vulnerabilities(agent_id, vulnerability_severity="Critical") + vulnerabilities = (await collect_agent_vulnerabilities(agent_id, vulnerability_severity=vulnerability_severity.value)).vulnerabilities # Create a CSV file logger.info(f"Creating CSV file for agent {agent_id} with {len(vulnerabilities)} vulnerabilities") logger.info(f"Vulnerabilities: {vulnerabilities}") @@ -505,10 +507,12 @@ async def get_agent_vulnerabilities_csv(agent_id: str, session: AsyncSession = D "CVE", "Status", "Title", + "EPSS Score", ], ) # Write the rows for vulnerability in vulnerabilities: + epss_score = await collect_epss_score(EpssThreatIntelRequest(cve=vulnerability.cve)) writer.writerow( [ vulnerability.severity, @@ -523,12 +527,13 @@ async def get_agent_vulnerabilities_csv(agent_id: str, session: AsyncSession = D vulnerability.cve, vulnerability.status, vulnerability.title, + epss_score.data[0].epss if epss_score.data else "", ], ) # Return the CSV file as a streaming response output.seek(0) return StreamingResponse( - content=output.getvalue(), + output, # Use the StringIO object directly media_type="text/csv", headers={"Content-Disposition": f"attachment; filename={agent_id}_vulnerabilities.csv"}, ) @@ -574,7 +579,67 @@ async def get_agent_sca_policy_results(agent_id: str, policy_id: str) -> WazuhAg logger.info(f"Fetching agent {agent_id} sca policy results") return await collect_agent_sca_policy_results(agent_id, policy_id) +@agents_router.get( + "/{agent_id}/csv/sca/{policy_id}", + description="Get agent sca results as CSV", + dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))], +) +async def get_agent_sca_policy_results_csv(agent_id: str, policy_id: str) -> StreamingResponse: + """ + Fetches the sca results of a specific agent and returns them as a CSV file. + + Args: + agent_id (str): The ID of the agent. + Returns: + StreamingResponse: The response containing the agent sca in CSV format. + """ + logger.info(f"Fetching agent {agent_id} sca policy results as CSV") + sca_results = (await collect_agent_sca_policy_results(agent_id, policy_id)).sca_policy_results + # Create a CSV file + logger.info(f"Creating CSV file for agent {agent_id} with {len(sca_results)} sca policy results") + output = io.StringIO() + writer = csv.writer(output) + # Write the header + writer.writerow( + [ + "Description", + "Policy ID", + "Reason", + "Command", + "Rationale", + "Condition", + "Title", + "Result", + "Remediation", + "Compliance", + "Rules", + ], + ) + # Write the rows + for sca_result in sca_results: + writer.writerow( + [ + sca_result.description, + sca_result.policy_id, + sca_result.reason, + sca_result.command, + sca_result.rationale, + sca_result.condition, + sca_result.title, + sca_result.result, + sca_result.remediation, + ', '.join([f"{compliance.key}: {compliance.value}" for compliance in sca_result.compliance]) if sca_result.compliance else "", + ', '.join([f"{rule.type}: {rule.rule}" for rule in sca_result.rules]) if sca_result.rules else "", + ], + ) + # Return the CSV file as a streaming response + output.seek(0) + return StreamingResponse( + output, # Use the StringIO object directly + media_type="text/csv", + headers={"Content-Disposition": f"attachment; filename={agent_id}_sca_policy_results.csv"}, + ) @agents_router.get(