PanoraysFindingsAPI
Retrieve and monitor internal security findings for your organization from the Panorays platform to streamline self-assessment posture and automate internal incident response within Cortex XSOAR.
Vulnerability Management · Panorays
Details
| ID | PanoraysFindingsAPI |
|---|---|
| Provider | Panorays |
| Category | Vulnerability Management |
| From Version | 6.0.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
README
Retrieve and monitor internal security findings for your organization from the Panorays platform to streamline self-assessment posture and automate internal incident response within Cortex XSOAR.
This integration was integrated and tested with version v2 of PanoraysFindingsAPI.
Configure Panorays Findings API in Cortex
| Parameter | Required |
|---|---|
| Panorays PAPI base URL | True |
| apikey | True |
| API Key | True |
| Trust any certificate (not secure) | False |
| Use system proxy settings | False |
| Maximum number of incidents to fetch per run | False |
| First fetch timestamp (e.g., 7 days) | False |
| Incident type | False |
| Fetch incidents | False |
Commands
You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.
panorays-finding-list
Lists the company findings as detected by Panorays.
Base Command
panorays-finding-list
Input
| Argument Name | Description | Required |
|---|---|---|
| limit | The maximum number of findings to return. Default is 50. | Optional |
| page | The page number of results to retrieve. Default is 1. | Optional |
Context Output
There is no context output for this command.
Configuration parameters
url— Panorays PAPI base URL (required)apikey— (required)insecure— Trust any certificate (not secure)proxy— Use system proxy settingsmax_fetch— Maximum number of incidents to fetch per runfirst_fetch— First fetch timestamp (e.g., 7 days)incidentType— Incident typeincidentFetchInterval— Incidents Fetch IntervalisFetch— Fetch incidents
Commands (1)
-
panorays-finding-listLists the company findings as detected by Panorays.
import demistomock as demisto # noqa: F401 from CommonServerPython import * # noqa: F401 import urllib3 import json from typing import Any from datetime import UTC urllib3.disable_warnings() class Client(BaseClient): def get_company_findings(self, limit: int, page: int) -> dict[str, Any]: params = {"limit": limit, "page": page} return self._http_request(method="GET", url_suffix="/v2/findings", params=params) def verify_module(client: Client) -> str: try: client._http_request("GET", "/v2/findings", params={"limit": 1}) return "ok" except Exception as e: if "Unauthorized" in str(e) or "Forbidden" in str(e): raise Exception("Authorization Error: check your API Key.") from e raise e def finding_list_command(client: Client, args: dict[str, Any]) -> CommandResults: limit = int(args.get("limit") or 50) page = int(args.get("page") or 1) raw_response = client.get_company_findings(limit=limit, page=page) findings = raw_response.get("data", []) markdown_data = [] for finding in findings: markdown_data.append( { "Finding ID": finding.get("id"), "Category": finding.get("category"), "Affected Asset": finding.get("asset_name"), "Risk Level": finding.get("severity"), "State": finding.get("status"), } ) markdown = tableToMarkdown(f"Panorays Findings (Page {page})", markdown_data, removeNull=True) return CommandResults( readable_output=markdown, outputs_prefix="Panorays.Finding", outputs_key_field="id", outputs=findings, raw_response=raw_response, ) def _make_aware(dt): """Ensure a datetime is timezone-aware (UTC). Returns None if dt is None.""" if dt is None: return None if dt.tzinfo is None: return dt.replace(tzinfo=UTC) return dt def fetch_incidents_command(client: Client, last_run: dict, first_fetch_time: str, max_fetch: int) -> tuple[dict, list[dict]]: last_fetch = last_run.get("last_fetch") if last_fetch: last_fetch_time = _make_aware(arg_to_datetime(last_fetch)) else: last_fetch_time = _make_aware(arg_to_datetime(first_fetch_time)) raw_response = client.get_company_findings(limit=max_fetch, page=1) findings = raw_response.get("data", []) incidents = [] latest_created_time = last_fetch_time for finding in findings: finding_time = _make_aware(arg_to_datetime(finding.get("insert_ts"))) if last_fetch_time and finding_time and finding_time <= last_fetch_time: continue incidents.append( { "name": f"Panorays Finding: {finding.get('asset_name', 'Unknown')}", "details": finding.get("finding_text", ""), "occurred": finding.get("insert_ts"), "rawJSON": json.dumps(finding), } ) if finding_time and (not latest_created_time or finding_time > latest_created_time): latest_created_time = finding_time next_run = {"last_fetch": latest_created_time.strftime("%Y-%m-%dT%H:%M:%SZ") if latest_created_time else last_fetch} return next_run, incidents def main() -> None: try: params = demisto.params() command = demisto.command() api_key = params.get("apikey") base_url = params.get("url", "https://api.panoraysapp.com") headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"} verify_certificate = not bool(params.get("insecure", False)) proxy = bool(params.get("proxy", False)) client = Client(base_url=base_url, verify=verify_certificate, proxy=proxy, headers=headers) if command == "test-module": return_results(verify_module(client)) elif command == "panorays-finding-list": return_results(finding_list_command(client, demisto.args())) elif command == "fetch-incidents": last_run = demisto.getLastRun() first_fetch_time = params.get("first_fetch", "3 days") max_fetch = int(params.get("max_fetch") or 50) next_run, incidents = fetch_incidents_command(client, last_run, first_fetch_time, max_fetch) demisto.setLastRun(next_run) demisto.incidents(incidents) except Exception as e: return_error(f"Error: {str(e)}") if __name__ in ("__main__", "__builtin__", "builtins"): main()