Neosec

Neosec is reinventing application security. Its pioneering SaaS platform gives security professionals visibility into behavior across their entire API estate. Built for organizations that expose APIs to partners, suppliers, and users, Neosec discovers all your APIs, analyzes their behavior, and stops threats lurking inside.

Network Security · Neosec

Details

IDNeosec
ProviderAkamai Technologies
CategoryNetwork Security
From Version6.5.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

Neosec is reinventing application security. Its pioneering SaaS platform gives security professionals visibility into behavior across their entire API estate. Built for organizations that expose APIs to partners, suppliers, and users, Neosec discovers all your APIs, analyzes their behavior, and stops threats lurking inside.

What does this pack do?
  • Ingests alerts from Neosec into XSOAR:
    • Neosec Posture Alerts covers OWASP top 10 and other vulnerabilities in API endpoints. Handled by R&D.
    • Neosec Runtime Alerts are on suspicious or malicious user behavior. Handled by SOC analysts.
  • Close/Reopens Neosec Alerts

Configure Neosec in Cortex

Parameter Description Required
API Key The API Key to use to connect to Neograph API True
URL Neograph API URL False
Tenant Key Tenant identifer (Tenant name) False
Fetch alerts with status Select the statuses of the alerts you wish to fetch (Open/Closed). False
Fetch alerts with type Select the types of the alerts you wish to fetch (Posture/Runtime). False
Severity of alerts to fetch Select the severities of the alerts you wish to fetch. False
Max incident to fetch   False
Incident type   False
Fetch incidents   False
First fetch time   False
De-tokenize alerts Select this if you use tokenization on PII data and wish to ingest Neosec alerts to XSOAR with detokenized data. False
Neosec Node URL If the ‘De-tokenize alets’ is selected, provide the url of the Neosec Node. for example http://[neosec node ip]:8080 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.

neosec-alert-status-set


Set alert status(Open, Closed)

Base Command

neosec-alert-status-set

Input

Argument Name Description Required
alert_id The alert id(UUID) in the Neosec platform. . Required
alert_status The alert status. Possible values are: Open, Closed. Required

Context Output

There is no context output for this command.

Configuration parameters

  • credentials — (required)
  • url — URL (required)
  • tenant_key — Tenant Key (required)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • alert_status — Fetch alerts with status
  • alert_type — Fetch alerts with type
  • severities — Severity of alerts to fetch
  • max_fetch — Max incident to fetch
  • first_fetch — First fetch time
  • neosec_node_url — Neosec Node URL (Optional)
  • incidentType — Incident type
  • incidentFetchInterval — Incidents Fetch Interval
  • isFetch — Fetch incidents

Commands (1)

  • neosec-alert-status-set

    Set alert status(Open, Closed)

"""Base Integration for Cortex XSOAR - Unit Tests file

Pytest Unit Tests: all funcion names must start with "test_"

More details: https://xsoar.pan.dev/docs/integrations/unit-testing

MAKE SURE YOU REVIEW/REPLACE ALL THE COMMENTS MARKED AS "TODO"

You must add at least a Unit Test function for every XSOAR command
you are implementing with your integration
"""

import json

import pytest
from Neosec import NeosecClient, NeosecNodeClient, fetch_incidents, set_alert_status

MOCK_URL = "http://123-fake-api.com"
MOCK_NODE_URL = "http://124-fake-api.com"
MOCK_TENANT_KEY = "fake_tenant"
MOCK_FIRST_TIME_TIMESTAMP = 1

MOCK_ALL_EVENTS = {
    "items": [
        {
            "id": "a29ceff4-52f3-efba-aabc-87909d0fbff2",
            "name": "Suspicious Privileged Operation Attempt",
            "timestamp": "2022-05-05T19:39:49.504000Z",
            "status": "Open",
            "severity": "Low",
            "description": "* Endpoint 'DELETE /v2/invoicing/invoices/{invoices_id}' in service 'Invoicing' \n"
            "* MerchantID '1c4ce8d6-5847-4f38-a689-4d83991ac3297' tried to access what might be"
            " high-privileged function \n* All 1 requested failed with '403 Forbbiden'  ",
            "category": "Data Access",
            "source": None,
            "author": "Analytics",
            "sequence_ids": None,
            "entities": [
                {
                    "value": "1c4ce8d6-5847-4f38-a689-4d83991ac329",
                    "name": "MerchantID",
                    "pretty_name": None,
                    "class": "user",
                    "family": "actor",
                    "value_type": "String",
                },
                {
                    "value": "E1SDajS6s1is7_13u+BW9uO_PHS80YQBj4nOpaXv9oOJ___KlA7pxr0WeAlQPiFeEi-"
                    "XQM1eLbHOlK_Uh0aZRcJvy6BFVmeCP",
                    "name": "AccessToken",
                    "pretty_name": None,
                    "class": "token",
                    "family": "actor",
                    "value_type": "String",
                },
                {"value": "127.0.0.1", "name": "IP", "pretty_name": None, "class": "ip", "family": "actor", "value_type": "IPv4"},
            ],
            "endpoints": [
                {
                    "id": "a94da4aa-52ce-b6d2-5c3a-55edcf1db288",
                    "method": "DELETE",
                    "endpoint_path": "/v2/invoicing/invoices/{invoices_id}",
                    "service_name": "Invoicing",
                    "labels": None,
                    "endpoint_labels": None,
                    "hidden_at": None,
                    "hidden_by": None,
                    "hidden": None,
                    "call_count": None,
                    "call_percentage": None,
                    "max_severity": None,
                    "first_seen": None,
                    "last_seen": None,
                }
            ],
            "caller_ips": ["127.0.0.1"],
            "labels": ["OWASP API1", "OWASP A5", "OWASP API5", "Data Leak", "PII"],
            "alert_type": "UserBehaviorAlert",
            "recommendations": "* Is the resource being accessed private to the actor entity type accessing it?"
            "\n* Is the actor entity accessing this resource an admin?\n* Investigate the"
            " behavior of the API consumer around the time of the alert",
            "alert_info": "Some functions and resources in the API are accessible only by high-privilege or "
            "admin users. When a low-privilege API consumer abnormally tries to use those functions,"
            " they may be looking for an authorization bypass vulnerability, often called BFLA"
            " (broken function level authorization).",
            "triggered_at": "2022-05-05T19:39:49.504000Z",
            "detection_model_id": "fa10ba69-89a0-4c15-876a-cdaf911fda25",
            "endpoint": "DELETE /v2/invoicing/invoices/{invoices_id}",
            "base_risk_score": 0,
            "call_ids": ["4fce6687-1c78-4083-a427-a82e1880d605"],
        },
        {
            "id": "a29ceff4-fccf-efba-aabc-87909d0fbff2",
            "name": "Brute Force Authentication Attempt",
            "timestamp": "2022-05-05T19:24:09.129000Z",
            "status": "Open",
            "severity": "Low",
            "description": "* Endpoint 'POST /v1/oauth2/token' in service 'Authentication'\n* It received 30"
            " or more bad requests in the last hour from IP 127.0.0.1\n* The average bad "
            "request ratio per 10 minutes per IP for this endpoint was 20 in the last 5 days.",
            "category": "Account Takeover",
            "source": None,
            "author": "Analytics",
            "sequence_ids": None,
            "entities": [
                {"value": "127.0.0.1", "name": "IP", "pretty_name": None, "class": "ip", "family": "actor", "value_type": "IPv4"}
            ],
            "endpoints": [
                {
                    "id": "88061ea4-665a-60ff-4db5-00a44694c99e",
                    "method": "POST",
                    "endpoint_path": "/v1/oauth2/token",
                    "service_name": "Authentication",
                    "labels": None,
                    "endpoint_labels": None,
                    "hidden_at": None,
                    "hidden_by": None,
                    "hidden": None,
                    "call_count": None,
                    "call_percentage": None,
                    "max_severity": None,
                    "first_seen": None,
                    "last_seen": None,
                }
            ],
            "caller_ips": ["127.0.0.1"],
            "labels": ["OWASP API2", "OWASP A2", "OWASP API4", "Unauthenticated EP"],
            "alert_type": "UserBehaviorAlert",
            "recommendations": "* Consider tightening rate limits for authentication endpoints\n* Look "
            "at adding IP addresses performing brute force attempts to your firewall's"
            " deny list\n* Any subsequent successful login from the alerted IP address "
            "should be treated as suspicious.",
            "alert_info": "In brute force attacks, malicious actors try to login to accounts using different"
            " passwords. These password could be part of a specified dictionary or simple "
            "guessing without any logic. This attack method is considered to be quite old"
            " but still effective and popular among hackers.",
            "triggered_at": "2022-05-05T19:24:09.129000Z",
            "detection_model_id": "a7d9f57d-b0d7-48ed-82a7-23db1b83271c",
            "endpoint": "POST /v1/oauth2/token",
            "base_risk_score": 0,
            "call_ids": [
                "0d9f54dd-5588-4462-8cc3-8fb4c0866eff",
                "35deff20-9646-4bf5-8a54-4947f913ac81",
                "717885d4-d5d6-455b-8a1f-5df60e8fff16",
                "2cdc6e23-6da6-4961-9ce8-88d0bc831ec1",
                "3b1969c0-1bfc-4817-8d70-b75bdc6494de",
                "0d8310ed-5318-45ab-912f-dff8f0c9f7d9",
                "715a5736-be61-4783-a5c2-1a2b709d7b8a",
                "d12fac0e-0693-4acb-bc08-c3607a1e648f",
                "9f2b35f3-173e-431a-b37a-0c9467c1a8c5",
                "0cefca46-6527-4c95-99c6-3c54d62e3b71",
                "68fe79ce-dc85-46a4-a210-76ccf3d73049",
                "d6f746f3-d72a-4a9f-a167-6c6724577554",
                "966b5ed6-38b0-43e7-beaa-9515a8d92f04",
                "6b79dea1-efa1-4961-84b1-a1db8f03e8f5",
                "18be3db1-0da4-475f-88b8-94729fe666eb",
                "84daa422-06ed-46ed-946a-29a1a11fbf84",
                "7c0ad902-eb04-42a8-8da3-eb932d963a41",
                "66f1b391-80db-424a-9fb6-af19aa6bc66c",
                "c66628fa-d65e-4a5c-beec-6aab578837ae",
                "01f66380-9c84-48dc-878a-b703ca3ceecb",
                "77dd2e77-6683-4a33-a48b-e3665b4e4b55",
                "b2e80ad7-edef-4201-819f-8ff0bc2a72be",
                "bc456b06-8efd-4e01-a087-593b9729cbf9",
                "b994df9f-8cff-4fed-a4ef-2e1e5595aaef",
                "8941f24f-0ff6-4ec8-995c-cd7a70486692",
                "0eea097f-5f58-4b8a-8d8a-9e72065d06c3",
                "08ba69e9-d90c-416e-8379-7c1a7b013dbc",
                "f97054f6-3894-4ce2-9834-4a41618b976a",
                "f432c9bd-e76d-4919-8ef4-970b9e9907cb",
                "b29e5267-354d-4d00-a164-2a6df8d2289d",
                "72d7daf8-9b00-46a0-b4c1-30a793515ab1",
                "700032ed-c209-434f-b006-f4a939d0fad5",
            ],
        },
        {
            "id": "a299aff4-52f3-efba-aabc-87909d0fbff2",
            "name": "Query Parameter Fuzzing",
            "timestamp": "2022-05-05T19:10:09.169000Z",
            "status": "Open",
            "severity": "Low",
            "description": "* Query parameter 'total_required' in endpoint 'GET /v2/invoicing/invoices' "
            "in service 'Invoicing'\n* IP '127.0.0.1' used 52 unique 'total_required'"
            " query parameter values in 10 minutes\n* 5 of IP requests returned 200 OK\n*"
            " On average, a single IP uses 6 unique query parameter values in 10 minutes, "
            "failing 2 of these requests.",
            "category": "Recon",
            "source": None,
            "author": "Analytics",
            "sequence_ids": None,
            "entities": [
                {
                    "value": "1c4ce8d6-5847-4f38-a689-4d83991ac329",
                    "name": "MerchantID",
                    "pretty_name": None,
                    "class": "user",
                    "family": "actor",
                    "value_type": "String",
                },
                {
                    "value": "E1SDajS6s1is7_13u+BW9uO_PHS80YQBj4nOpaXv9oOJ___"
                    "KlA7pxr0WeAlQPiFeEi-XQM1eLbHOlK_Uh0aZRcJvy6BFVmeCP",
                    "name": "AccessToken",
                    "pretty_name": None,
                    "class": "token",
                    "family": "actor",
                    "value_type": "String",
                },
                {"value": "127.0.0.1", "name": "IP", "pretty_name": None, "class": "ip", "family": "actor", "value_type": "IPv4"},
            ],
            "endpoints": [
                {
                    "id": "da1316f5-ab94-b2cb-67e6-ee30a832acd7",
                    "method": "GET",
                    "endpoint_path": "/v2/invoicing/invoices",
                    "service_name": "Invoicing",
                    "labels": None,
                    "endpoint_labels": None,
                    "hidden_at": None,
                    "hidden_by": None,
                    "hidden": None,
                    "call_count": None,
                    "call_percentage": None,
                    "max_severity": None,
                    "first_seen": None,
                    "last_seen": None,
                }
            ],
            "caller_ips": ["127.0.0.1"],
            "labels": ["OWASP API1", "OWASP API4", "OWASP A5", "PII"],
            "alert_type": "UserBehaviorAlert",
            "recommendations": "* Check whether the fuzzing originated from a company-sanctioned "
            "tool, or was part of an authorized penetration test\n* Has the same"
            " actor performed any subsequent operations against your APIs? If so,"
            " treat them as suspicious.",
            "alert_info": "Attackers fuzz query parameter values in order to find injection points "
            "and whether the API is leaking any information. An extreme case of information"
            " leaks is broken authorization, when attacker can access resources they should "
            "not be able to access.\nThis detection model triggers on an API consumer fuzzing a "
            "certain endpoint query parameter, and getting one or more successful responses."
            " This may mean that the attacker managed to access unauthorized data. Note that"
            " regardless of the success or failure of the calls themselves - the attacker may "
            "also have learned important information from the response codes, sizes, and other"
            " response attributes, such as the time it took to process each request.",
            "triggered_at": "2022-05-05T19:10:09.169000Z",
            "detection_model_id": "96c02fc1-430a-4dd5-8aef-72d42927620d",
            "endpoint": "GET /v2/invoicing/invoices",
            "base_risk_score": 0,
            "call_ids": [
                "f58c352f-f115-4aa9-a302-9d8e3596ec73",
                "f6ad2b35-8d55-461b-923d-301b6538637e",
                "3b2bcab2-84e1-481d-b524-1585afcd15d4",
                "217d4279-4c9d-4114-93cd-37d24ba8aa97",
                "6e9c3908-2fd9-4a04-a91c-0eedb1d3591f",
                "59b146f7-77eb-4e6c-a546-23f386fc1294",
                "a72c9d5d-fd08-447a-aa79-7799218ca99f",
                "ab9d501a-f277-4977-982b-ca97eae6addb",
                "f4b0ddc3-3c45-4ccb-b0ac-39b8b7c9ac69",
                "20a81ae7-96c4-43ae-9d59-e5850453e091",
                "fca1ad1d-bbc2-49bd-ac20-fccb3e323ad3",
                "e0fedd91-e28e-4167-a5f9-32390b287525",
                "3dcd7e30-03a0-40d2-a86f-a37fa863fbfd",
                "cc26945b-0e6e-4d04-a6d4-76712289c34e",
                "3af8da7d-e7c4-4ac6-bdd0-ecdd11d465d6",
                "8e3cfd4b-a416-4377-ba5c-34f3ae31ba28",
                "8de8bd9f-ffc1-4f6f-9945-3da5d89a7686",
                "9fb738f0-ef36-49a8-81c3-45fc2477dbd8",
                "ba099b16-ceea-4d5c-8c95-a156505e75eb",
                "6eee550d-b0b9-4f51-9aa4-0f4c1db564dd",
                "8355c0f4-55b2-4a32-8cbb-13494cc6d233",
                "3b3ca9dc-617f-430d-ba9c-410f5b412939",
                "6d06626a-50a5-4cd1-b7b7-71451851fe23",
                "0608201d-26d9-4cde-8c45-2be10e36ddcc",
                "2cbf0170-60a5-4c65-92d0-dd809ec47076",
                "4318f110-7ce1-43de-804b-8d8c9c969ff9",
                "0836e94c-3cec-4281-84ef-7d598777bf27",
                "fc2bcdfc-83c9-4c76-a75d-dec48c2880ba",
                "ad2caccc-3fbf-4686-bbc8-03023c2317d8",
                "b00341c5-343b-46f8-9777-165d42f8c9f2",
                "29312ed3-073c-4af1-8751-40add16836d6",
                "77f4c74b-3300-4c87-a045-e22eb9d8846c",
                "ea478030-eccc-43e0-9fdb-a4776fc1ae9f",
                "1cbc4e9f-0654-435d-a8d0-13d9e613ee32",
                "d8a08127-c72b-42f6-bf72-47d815f73fa5",
                "2eebe086-ef2e-4a34-a4c8-72f68746d9ac",
                "92c6b2d3-235d-445b-b140-cc2c58535496",
                "1069a3f7-035f-46c4-a51d-1e19e132fff0",
                "623e1428-5d88-4c67-8af9-65249b147300",
                "41d9b961-013e-43da-9920-e0759ecb8534",
                "e3efb87d-b69d-4bf6-ac63-2d6fa7d28f3f",
                "2220d185-41df-474f-ba1f-968c7fb44ea7",
                "6e9fcc02-d1c5-49fe-9481-cf76670a4b54",
                "f69c833e-3157-4142-a199-af1200b7d76d",
                "becc19f7-0349-4872-947b-f128ae58b579",
                "1d1cdc41-4eb0-40f9-9b2c-b2d25a5a00bb",
                "ae586ed6-45ba-41cd-8e17-017127c060e9",
                "f32b0726-4e68-4bd8-a3cf-f45d18901189",
                "a382109b-651e-4cc6-a7e7-be4cbbda18c1",
                "39ae1133-0ae2-4b15-9396-cb8b6bc771d2",
                "455f84d5-1052-4555-9c9c-71b0ea51f61e",
                "b10b7571-8de6-4fe7-a25b-03bd3ee5c98b",
            ],
        },
        {
            "id": "a299b804-52f3-48eb-abd1-87909d0f9ffd",
            "name": "Shadow Parameter",
            "timestamp": "2022-05-05T18:55:22.148000Z",
            "status": "Open",
            "severity": "Low",
            "description": "* Endpoint 'GET /v2/checkout/orders/{order_id}' in service 'Invoicing'\n* The "
            "following query parameters are not documented: full_details\n* They had been"
            " used successfully 5 times in the last 30 days",
            "category": "Shadow API",
            "source": None,
            "author": "Analytics",
            "sequence_ids": None,
            "entities": [],
            "endpoints": [
                {
                    "id": "997c741b-9cd9-df22-0a34-2e078fcead86",
                    "method": "GET",
                    "endpoint_path": "/v2/checkout/orders/{orders_id}",
                    "service_name": "Orders",
                    "labels": None,
                    "endpoint_labels": None,
                    "hidden_at": None,
                    "hidden_by": None,
                    "hidden": None,
                    "call_count": None,
                    "call_percentage": None,
                    "max_severity": None,
                    "first_seen": None,
                    "last_seen": None,
                }
            ],
            "caller_ips": ["127.0.0.1"],
            "labels": ["OWASP API9", "OWASP API7", "OWASP A6", "PII", "Money Out"],
            "alert_type": "APIAlert",
            "recommendations": "* Review the endpoint and assess whether the parameter(s) should be exposed\n*"
            " Update the documentation, preferably adding documentation generation into your"
            " CI/CD pipeline",
            "alert_info": "Shadow parameters are undocumented parameters accepted by a documented API endpoint."
            " These undocumented parameters may not have been tested as thoroughly as the documented"
            " parameters, and therefore pose a greater security risk.",
            "triggered_at": "2022-05-05T18:55:22.148000Z",
            "detection_model_id": "5a78eeb0-8be0-434f-9ddb-96f3e9c97a3e",
            "endpoint": "GET /v2/checkout/orders/{orders_id}",
            "base_risk_score": 15,
            "call_ids": [
                "a58e2b82-843f-4115-99a3-61754b9b40ad",
                "73ab15a8-6292-4aff-85b8-5851f415c77d",
                "28bc4173-c3e2-45aa-b0cc-4ca40a85e9e8",
                "bc9a0fc4-e2ab-41c9-baf9-59d0a2e3dd56",
                "77e70698-805e-4fa1-a228-fc123f823940",
            ],
        },
    ],
    "count": 4,
    "total": 4,
}

MOCK_NODE_ALL_EVENTS = {"Message": json.dumps(MOCK_ALL_EVENTS["items"])}
MOCK_ALERT_ID = "a299b804-52f3-48eb-abd1-87909d0f9ffd"
MOCK_NODE_HEALTH_CHECK = {"Status": "ok"}
MOCK_NODE_HEALTH_CHECK_FAILED = {"Status": "failed"}
MOCK_ALL_EVENTS_INVALID_TRIGGERED_AT = {
    "items": [
        {
            "id": "a29ceff4-52f3-efba-aabc-87909d0fbff2",
            "name": "Suspicious Privileged Operation Attempt",
            "timestamp": "2022-05-05T19:39:49.504000Z",
            "status": "Open",
            "severity": "Low",
            "description": "* Endpoint 'DELETE /v2/invoicing/invoices/{invoices_id}' in service 'Invoicing' \n"
            "* MerchantID '1c4ce8d6-5847-4f38-a689-4d83991ac3297' tried to access what might be"
            " high-privileged function \n* All 1 requested failed with '403 Forbbiden'  ",
            "category": "Data Access",
            "source": None,
            "author": "Analytics",
            "sequence_ids": None,
            "entities": [
                {
                    "value": "1c4ce8d6-5847-4f38-a689-4d83991ac329",
                    "name": "MerchantID",
                    "pretty_name": None,
                    "class": "user",
                    "family": "actor",
                    "value_type": "String",
                },
                {
                    "value": "E1SDajS6s1is7_13u+BW9uO_PHS80YQBj4nOpaXv9oOJ___KlA7pxr0WeAlQPiFeEi-"
                    "XQM1eLbHOlK_Uh0aZRcJvy6BFVmeCP",
                    "name": "AccessToken",
                    "pretty_name": None,
                    "class": "token",
                    "family": "actor",
                    "value_type": "String",
                },
                {"value": "127.0.0.1", "name": "IP", "pretty_name": None, "class": "ip", "family": "actor", "value_type": "IPv4"},
            ],
            "endpoints": [
                {
                    "id": "a94da4aa-52ce-b6d2-5c3a-55edcf1db288",
                    "method": "DELETE",
                    "endpoint_path": "/v2/invoicing/invoices/{invoices_id}",
                    "service_name": "Invoicing",
                    "labels": None,
                    "endpoint_labels": None,
                    "hidden_at": None,
                    "hidden_by": None,
                    "hidden": None,
                    "call_count": None,
                    "call_percentage": None,
                    "max_severity": None,
                    "first_seen": None,
                    "last_seen": None,
                }
            ],
            "caller_ips": ["127.0.0.1"],
            "labels": ["OWASP API1", "OWASP A5", "OWASP API5", "Data Leak", "PII"],
            "alert_type": "UserBehaviorAlert",
            "recommendations": "* Is the resource being accessed private to the actor entity type accessing it?"
            "\n* Is the actor entity accessing this resource an admin?\n* Investigate the"
            " behavior of the API consumer around the time of the alert",
            "alert_info": "Some functions and resources in the API are accessible only by high-privilege or "
            "admin users. When a low-privilege API consumer abnormally tries to use those functions,"
            " they may be looking for an authorization bypass vulnerability, often called BFLA"
            " (broken function level authorization).",
            "triggered_at": "201322-05-05T19:39:49.504000Z",
            "detection_model_id": "fa10ba69-89a0-4c15-876a-cdaf911fda25",
            "endpoint": "DELETE /v2/invoicing/invoices/{invoices_id}",
            "base_risk_score": 0,
            "call_ids": ["4fce6687-1c78-4083-a427-a82e1880d605"],
        }
    ],
    "count": 4,
    "total": 4,
}


def test_first_fetch_incidents(requests_mock):
    requests_mock.post(MOCK_URL + f"/organizations/{MOCK_TENANT_KEY}/alerts/query", json=MOCK_ALL_EVENTS)

    client = NeosecClient(base_url=MOCK_URL, verify=True, proxy=False, tenant_key=MOCK_TENANT_KEY, headers={})

    next_run, incidents = fetch_incidents(
        client=client, node_client=None, max_results=50, last_run={}, first_fetch_time=MOCK_FIRST_TIME_TIMESTAMP
    )

    assert len(incidents) == 4
    assert json.loads(incidents[3]["rawJSON"])["id"] == "a299b804-52f3-48eb-abd1-87909d0f9ffd"


def test_first_fetch_incidents_invalid_triggered_at(requests_mock):
    requests_mock.post(MOCK_URL + f"/organizations/{MOCK_TENANT_KEY}/alerts/query", json=MOCK_ALL_EVENTS_INVALID_TRIGGERED_AT)

    client = NeosecClient(base_url=MOCK_URL, verify=True, proxy=False, tenant_key=MOCK_TENANT_KEY, headers={})

    with pytest.raises(ValueError):
        fetch_incidents(client=client, node_client=None, max_results=50, last_run={}, first_fetch_time=MOCK_FIRST_TIME_TIMESTAMP)


def test_first_fetch_incidents_with_filters(requests_mock):
    requests_mock.post(MOCK_URL + f"/organizations/{MOCK_TENANT_KEY}/alerts/query", json=MOCK_ALL_EVENTS)

    client = NeosecClient(base_url=MOCK_URL, verify=True, proxy=False, tenant_key=MOCK_TENANT_KEY, headers={})

    next_run, incidents = fetch_incidents(
        client=client,
        node_client=None,
        max_results=50,
        last_run={},
        first_fetch_time=MOCK_FIRST_TIME_TIMESTAMP,
        alert_status="Open",
        severities=["Info"],
        alert_type=["Posture"],
    )

    assert len(incidents) == 4
    assert json.loads(incidents[3]["rawJSON"])["id"] == "a299b804-52f3-48eb-abd1-87909d0f9ffd"


def test_next_fetch(requests_mock):
    requests_mock.post(MOCK_URL + f"/organizations/{MOCK_TENANT_KEY}/alerts/query", json=MOCK_ALL_EVENTS)

    client = NeosecClient(base_url=MOCK_URL, verify=True, proxy=False, tenant_key=MOCK_TENANT_KEY, headers={})

    next_run, incidents = fetch_incidents(
        client=client,
        node_client=None,
        last_run={"last_fetch": MOCK_FIRST_TIME_TIMESTAMP},
        first_fetch_time=MOCK_FIRST_TIME_TIMESTAMP,
        max_results=50,
    )

    assert len(incidents) == 4
    assert json.loads(incidents[3]["rawJSON"])["id"] == "a299b804-52f3-48eb-abd1-87909d0f9ffd"


def test_first_fetch_incidents_with_detok(requests_mock):
    requests_mock.post(MOCK_URL + f"/organizations/{MOCK_TENANT_KEY}/alerts/query", json=MOCK_ALL_EVENTS)

    requests_mock.post(MOCK_NODE_URL + "/detokenize", json=MOCK_NODE_ALL_EVENTS)

    client = NeosecClient(base_url=MOCK_URL, verify=True, proxy=False, tenant_key=MOCK_TENANT_KEY, headers={})

    node_client = NeosecNodeClient(
        base_url=MOCK_NODE_URL,
        verify=True,
        proxy=False,
    )

    next_run, incidents = fetch_incidents(
        client=client, node_client=node_client, max_results=50, last_run={}, first_fetch_time=MOCK_FIRST_TIME_TIMESTAMP
    )

    assert len(incidents) == 4
    assert json.loads(incidents[3]["rawJSON"])["id"] == "a299b804-52f3-48eb-abd1-87909d0f9ffd"


def test_test_module_sanity(requests_mock):
    from Neosec import test_module

    requests_mock.post(MOCK_URL + f"/organizations/{MOCK_TENANT_KEY}/alerts/query", json=MOCK_ALL_EVENTS)

    client = NeosecClient(base_url=MOCK_URL, verify=True, proxy=False, tenant_key=MOCK_TENANT_KEY, headers={})

    result = test_module(client, None, None, None, None, 50)
    assert result == "ok"


def test_test_module_with_detok_sanity(requests_mock):
    from Neosec import test_module

    requests_mock.post(MOCK_URL + f"/organizations/{MOCK_TENANT_KEY}/alerts/query", json=MOCK_ALL_EVENTS)
    requests_mock.get(MOCK_NODE_URL + "/healthcheck", json=MOCK_NODE_HEALTH_CHECK)

    client = NeosecClient(base_url=MOCK_URL, verify=True, proxy=False, tenant_key=MOCK_TENANT_KEY, headers={})
    node_client = NeosecNodeClient(
        base_url=MOCK_NODE_URL,
        verify=True,
        proxy=False,
    )

    result = test_module(client, node_client, None, None, None, 50)
    assert result == "ok"


def test_test_module_with_detok_failed(requests_mock):
    from Neosec import test_module

    requests_mock.post(MOCK_URL + f"/organizations/{MOCK_TENANT_KEY}/alerts/query", json=MOCK_ALL_EVENTS)
    requests_mock.get(MOCK_NODE_URL + "/healthcheck", json=MOCK_NODE_HEALTH_CHECK_FAILED)

    client = NeosecClient(base_url=MOCK_URL, verify=True, proxy=False, tenant_key=MOCK_TENANT_KEY, headers={})
    node_client = NeosecNodeClient(
        base_url=MOCK_NODE_URL,
        verify=True,
        proxy=False,
    )

    result = test_module(client, node_client, None, None, None, 50)
    assert result != "ok"


def test_set_alert_status_command(requests_mock):
    requests_mock.patch(MOCK_URL + f"/organizations/{MOCK_TENANT_KEY}/alerts/{MOCK_ALERT_ID}", json={})

    client = NeosecClient(base_url=MOCK_URL, verify=True, proxy=False, tenant_key=MOCK_TENANT_KEY, headers={})
    set_alert_status(client, MOCK_ALERT_ID, "Closed")