SearchIssuesAgentix

Searches Cortex Issues.

python · Common Scripts

Details

IDSearchIssuesAgentix
Languagepython
From Version5.0.0
Docker Imagedemisto/python3:3.12.13.10116658
TagsUtility
from unittest.mock import MagicMock

from CommonServerPython import *
import pytest

from Packs.CommonScripts.Scripts.SearchIssues import SearchIssues
from SearchIssues import *


def test_create_sha_search_field_query_single_value():
    result = create_sha_search_field_query("actor_process_image_sha256", EQ, ["abc123"])
    expected = {"AND": [{"OR": [{"SEARCH_FIELD": "actor_process_image_sha256", "SEARCH_TYPE": "EQ", "SEARCH_VALUE": "abc123"}]}]}
    assert result == expected


def test_create_sha_search_field_query_multiple_values():
    result = create_sha_search_field_query("actor_process_image_sha256", EQ, ["abc123", "def456"])
    expected = {
        "AND": [
            {
                "OR": [
                    {"SEARCH_FIELD": "actor_process_image_sha256", "SEARCH_TYPE": "EQ", "SEARCH_VALUE": "abc123"},
                    {"SEARCH_FIELD": "actor_process_image_sha256", "SEARCH_TYPE": "EQ", "SEARCH_VALUE": "def456"},
                ]
            }
        ]
    }
    assert result == expected


def test_create_sha_search_field_query_empty_list():
    result = create_sha_search_field_query("actor_process_image_sha256", EQ, [])
    assert result is None


def test_prepare_sha256_custom_field_empty_input():
    args = {"sha256": None}
    result = prepare_sha256_custom_field(args)
    assert result is None


@pytest.mark.parametrize("sha_values", [["abc123", "xyz456"]])
def test_main_with_sha256_filter(monkeypatch, sha_values):
    expected_custom_filter = """{
  "OR": [
    {
      "AND": [
        {
          "OR": [
            {
              "SEARCH_FIELD": "actor_process_image_sha256",
              "SEARCH_TYPE": "EQ",
              "SEARCH_VALUE": "abc123"
            },
            {
              "SEARCH_FIELD": "actor_process_image_sha256",
              "SEARCH_TYPE": "EQ",
              "SEARCH_VALUE": "xyz456"
            }
          ]
        }
      ]
    },
    {
      "AND": [
        {
          "OR": [
            {
              "SEARCH_FIELD": "causality_actor_process_image_sha256",
              "SEARCH_TYPE": "EQ",
              "SEARCH_VALUE": "abc123"
            },
            {
              "SEARCH_FIELD": "causality_actor_process_image_sha256",
              "SEARCH_TYPE": "EQ",
              "SEARCH_VALUE": "xyz456"
            }
          ]
        }
      ]
    },
    {
      "AND": [
        {
          "OR": [
            {
              "SEARCH_FIELD": "action_process_image_sha256",
              "SEARCH_TYPE": "EQ",
              "SEARCH_VALUE": "abc123"
            },
            {
              "SEARCH_FIELD": "action_process_image_sha256",
              "SEARCH_TYPE": "EQ",
              "SEARCH_VALUE": "xyz456"
            }
          ]
        }
      ]
    },
    {
      "AND": [
        {
          "OR": [
            {
              "SEARCH_FIELD": "os_actor_process_image_sha256",
              "SEARCH_TYPE": "EQ",
              "SEARCH_VALUE": "abc123"
            },
            {
              "SEARCH_FIELD": "os_actor_process_image_sha256",
              "SEARCH_TYPE": "EQ",
              "SEARCH_VALUE": "xyz456"
            }
          ]
        }
      ]
    },
    {
      "AND": [
        {
          "OR": [
            {
              "SEARCH_FIELD": "action_file_macro_sha256",
              "SEARCH_TYPE": "EQ",
              "SEARCH_VALUE": "abc123"
            },
            {
              "SEARCH_FIELD": "action_file_macro_sha256",
              "SEARCH_TYPE": "EQ",
              "SEARCH_VALUE": "xyz456"
            }
          ]
        }
      ]
    }
  ]
}"""

    # Mock demisto.args()
    monkeypatch.setattr(
        SearchIssues.demisto,
        "args",
        lambda: {"sha256": ",".join(sha_values), "start_time": "2024-01-01", "end_time": "2024-01-02"},
    )

    # Mock demisto.executeCommand()
    fake_response = {
        "EntryContext": {
            "Core.Issue(val.internal_id && val.internal_id == obj.internal_id)": [{"internal_id": "test123", "severity": "high"}]
        },
        "HumanReadable": "Sample Issue Result",
        "Type": 1,
    }

    execute_command_mock = MagicMock(return_value=[fake_response])
    monkeypatch.setattr(SearchIssues.demisto, "executeCommand", execute_command_mock)

    # Mock demisto.debug and return_results
    monkeypatch.setattr(SearchIssues, "return_results", lambda x: x)
    monkeypatch.setattr(SearchIssues.demisto, "debug", lambda x: None)

    SearchIssues.main()

    # Ensure core-get-issues was called
    assert execute_command_mock.called, "core-get-issues was not called"

    # Get the actual args passed to core-get-issues
    called_args = execute_command_mock.call_args[0][1]  # [0] is args tuple, [1] is the args dict

    assert "custom_filter" in called_args, "custom_filter not found in args"

    expected_dict = json.loads(expected_custom_filter)
    actual_dict = json.loads(called_args["custom_filter"])

    assert actual_dict == expected_dict, "custom_filter structure does not match expected"


@pytest.mark.parametrize(
    "args, expected",
    [
        # empty string values are dropped
        ({"status": "", "severity": "high"}, {"severity": "high"}),
        # all empty → empty result
        ({"status": "", "page": ""}, {}),
        # empty input dict
        ({}, {}),
        # non-empty regular args are kept as-is
        ({"status": "active", "severity": "low"}, {"status": "active", "severity": "low"}),
    ],
)
def test_remove_empty_string_values_general(args, expected):
    assert remove_empty_string_values(args) == expected


@pytest.mark.parametrize(
    "key, value, should_keep",
    [
        ("page", "1", True),
        ("page", "0", True),
        ("page_size", "100", True),
        ("page", " 3 ", True),
        ("page_size", " 10 ", True),
        ("page", "n/a", False),
        ("page", "invalid_offset", False),
        ("page", "1.5", False),
        ("page", "-1", False),
        ("page", "abc", False),
        ("page_size", "bad", False),
        ("page", "", False),
        ("page_size", "", False),
    ],
)
def test_remove_empty_string_values_numeric_args(key, value, should_keep):
    result = remove_empty_string_values({key: value})
    if should_keep:
        assert result == {key: value}
    else:
        assert result == {}


@pytest.mark.parametrize(
    "value, should_keep",
    [
        ("42", True),
        ("1,2,3", True),
        (["10", "20"], True),
        ("n/a", False),
        ("abc", False),
        ("1,abc,3", False),
        (["10", "n/a"], False),
        ("", False),
    ],
)
def test_remove_empty_string_values_numeric_list_args(value, should_keep):
    result = remove_empty_string_values({"issue_id": value})
    if should_keep:
        assert result == {"issue_id": value}
    else:
        assert result == {}


@pytest.mark.parametrize(
    "args, expected",
    [
        (
            # valid regular + valid numeric + valid list
            {"status": "active", "page": "2", "issue_id": "7,8"},
            {"status": "active", "page": "2", "issue_id": "7,8"},
        ),
        (
            # empty string dropped, invalid numeric dropped, valid list kept
            {"severity": "", "page_size": "bad", "issue_id": "1,2"},
            {"issue_id": "1,2"},
        ),
        (
            # all three categories invalid → empty result
            {"severity": "", "page": "n/a", "issue_id": "abc"},
            {},
        ),
        (
            # numeric arg valid, list arg invalid, regular arg kept
            {"page": "5", "issue_id": "x,y", "status": "closed"},
            {"page": "5", "status": "closed"},
        ),
    ],
)
def test_remove_empty_string_values_mixed(args, expected):
    assert remove_empty_string_values(args) == expected