QSS

QSS integration helps you to fetch Cases from Q-SCMP and add new cases automatically through XSOAR.

Utilities · Quantum Security Systems

Details

IDQSS
ProviderOpen Source
CategoryUtilities
From Version6.0.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

QSS integration helps you to fetch Cases from Q-SCMP and add new cases automatically through XSOAR.
This integration was integrated and tested with version 3.6 of Q-SCMP. Please contact your platform administrtor to enable Cortex XSOAR integration.

Configure QSS on Cortex XSOAR

Prerequisites

  1. Please contact your Q-SCMP platform administrtor to obtain Cortex API Key.
  2. Please contact your Q-SCMP platform administrtor to obtain Cortex Server URL.

Configure Q-SCMP on Cortex XSOAR

  1. Navigate to Settings > Integrations > Servers & Services.
  2. Search for QSS.
  3. Click Add instance to create and configure a new integration instance.

    Parameter Required
    Server URL (e.g. https://) True
    Fetch incidents False
    Incident type False
    Max fetch False
    API Key True
    Fetch cases with status (Open, Closed) False
    Minimum severity of cases to fetch False
    Flase positive cases to fetch False
    Back time duration of cases to fetch (Hours) True
    First fetch time False
    Trust any certificate (not secure) False
    Use system proxy settings False
  4. Click Test to validate the URLs, token, and connection.

Use Cases

  1. Fetch SOC cases to Cortex SOAR based on the case severity.
  2. Fetch SOC cases to Cortex SOAR based on the case status.
  3. Fetch SOC cases to Cortex SOAR based on the case false positive flag.

Fetched Cases Data

  1. Case ID
  2. Case Creation Date
  3. Case Number
  4. Case Category
  5. Case Sub Category
  6. Case Severity
  7. Case Status
  8. Case Title
  9. Case Assignee
  10. Case False Positive
  11. Case Created By
  12. Case Last Update
  13. Case TLP
  14. Case Description
  15. Case Notes
  16. Case Tags
  17. Case Custom Attributes
  18. Case Assets
  19. Case IOCs

Configuration parameters

  • url — Server URL (e.g. https://example.com) (required)
  • isFetch — Fetch incidents
  • incidentType — Incident type
  • incidentFetchInterval — Incidents Fetch Interval
  • apikey — API Key (required)
  • severity — Minimum severity of cases to fetch
  • max_fetch — Max fetch
  • status — Fetch cases with status (Open, Closed)
  • false_positive — Flase positive cases to fetch
  • first_fetch — First fetch time
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (0)

This integration defines no commands.

import demistomock as demisto  # noqa: F401
from CommonServerPython import *  # noqa: F401


import json
import urllib3
import traceback
from typing import Any, cast

# Disable insecure warnings
urllib3.disable_warnings()


""" CONSTANTS """


DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
MAX_INCIDENTS_TO_FETCH = 50
QSS_SEVERITIES = ["Low", "Medium", "High", "Critical"]

""" CLIENT CLASS """


class Client(BaseClient):
    def search_alerts(
        self,
        alert_status: str | None,
        severity: str | None,
        max_results: int | None,
        start_time: int | None,
        api_key: str | None,
        false_positive: str | None,
    ) -> list[dict[str, Any]]:
        request_params: dict[str, Any] = {}

        if api_key:
            request_params["apikey"] = api_key

        if start_time:
            request_params["start_time"] = start_time

        if severity:
            request_params["severity"] = severity

        if alert_status:
            request_params["status"] = alert_status

        if max_results:
            request_params["max_fetch"] = max_results

        if false_positive:
            request_params["false_positive"] = false_positive

        demisto.debug(f"Command being called is {demisto.command()}")

        return self._http_request(method="GET", url_suffix="/rest/noauth/third_party/read_object/xsoar/v1", params=request_params)


""" HELPER FUNCTIONS """


def convert_to_demisto_severity(severity: str) -> int:
    return {
        "Low": 1,  # low severity
        "Medium": 2,  # medium severity
        "High": 3,  # high severity
        "Critical": 4,  # critical severity
    }[severity]


def test_module(client: Client, first_fetch_time: int, api_key: str) -> str:
    try:
        client.search_alerts(
            max_results=1, alert_status=None, severity=None, start_time=first_fetch_time, api_key=api_key, false_positive=None
        )
    except DemistoException as e:
        if "Forbidden" in str(e):
            return "Authorization Error: make sure API Key is correctly set"
        else:
            raise e
    return "ok"


def fetch_incidents(
    client: Client,
    max_results: int,
    last_run: dict[str, int],
    first_fetch_time: int | None,
    alert_status: str | None,
    min_severity: str,
    api_key: str | None,
    false_positive: str | None,
) -> tuple[dict[str, int], list[dict]]:
    last_fetch = last_run.get("last_fetch", None)

    if last_fetch is None:
        last_fetch = first_fetch_time
    else:
        last_fetch = int(last_fetch)

    latest_created_time = cast(int, last_fetch)
    incidents: list[dict[str, Any]] = []
    alerts = client.search_alerts(
        alert_status=alert_status,
        max_results=max_results,
        start_time=last_fetch,
        severity=min_severity,
        false_positive=false_positive,
        api_key=api_key,
    )

    demisto.debug("Alerts Fetched")

    for alert in alerts:
        incident_created_time = int(alert.get("last_update_sec", "0"))

        if last_fetch and incident_created_time <= last_fetch:
            continue

        incident_name = "SOC Case " + str(alert.get("reference"))

        demisto.debug("JSON debug alert")
        demisto.debug(json.dumps(alert))
        incident = {
            "name": incident_name,
            "occurred": alert.get("created"),
            "event_id": alert.get("id"),
            "rawJSON": json.dumps(alert),
            "severity": convert_to_demisto_severity(alert.get("severity", "Low")),
        }

        incidents.append(incident)

        if incident_created_time > latest_created_time:
            latest_created_time = incident_created_time

    next_run = {"last_fetch": latest_created_time}
    return next_run, incidents


""" MAIN FUNCTION """


def main() -> None:
    """main function, parses params and runs command functions

    :return:
    :rtype:
    """

    base_url = urljoin(demisto.params()["url"], "")

    verify_certificate = not demisto.params().get("insecure", False)

    first_fetch_time = arg_to_datetime(
        arg=demisto.params().get("first_fetch", "3 days"), arg_name="First fetch time", required=True
    )

    first_fetch_timestamp = int(first_fetch_time.timestamp()) if first_fetch_time else None
    assert isinstance(first_fetch_timestamp, int)

    proxy = demisto.params().get("proxy", False)

    try:
        api_key = demisto.params().get("apikey")

        headers: dict[str, Any] = {
            # No need for headers in the current version of integration
        }
        client = Client(base_url=base_url, verify=verify_certificate, headers=headers, proxy=proxy)

        if demisto.command() == "test-module":
            # This is the call made when pressing the integration Test button.
            result = test_module(client, first_fetch_timestamp, api_key)
            return_results(result)

        elif demisto.command() == "fetch-incidents":
            # Set and define the fetch incidents command to run after activated via integration settings.
            alert_status = demisto.params().get("status")
            min_severity = demisto.params().get("severity")
            false_positive = demisto.params().get("false_positive")

            # Convert the argument to an int using helper function or set to MAX_INCIDENTS_TO_FETCH
            max_results = arg_to_number(arg=demisto.params().get("max_fetch"), arg_name="max_fetch", required=False)
            if not max_results or max_results > MAX_INCIDENTS_TO_FETCH:
                max_results = MAX_INCIDENTS_TO_FETCH

            next_run, incidents = fetch_incidents(
                client=client,
                max_results=max_results,
                last_run=demisto.getLastRun(),  # getLastRun() gets the last run dict
                first_fetch_time=first_fetch_timestamp,
                alert_status=alert_status,
                min_severity=min_severity,
                api_key=api_key,
                false_positive=false_positive,
            )

            # saves next_run for the time fetch-incidents is invoked
            demisto.setLastRun(next_run)
            # fetch-incidents calls ``demisto.incidents()`` to provide the list
            # of incidents to crate
            demisto.incidents(incidents)

    # Log exceptions and return errors
    except Exception as e:
        demisto.error(traceback.format_exc())  # print the traceback
        return_error(f"Failed to execute {demisto.command()} command.\nError:\n{str(e)}")


""" ENTRY POINT """


if __name__ in ("__main__", "__builtin__", "builtins"):
    main()