Armis

Use the Armis integration to search alerts and devices, tag and untag devices, and set alert statuses.

Utilities · Armis

Details

IDArmis
ProviderInsight Partners
CategoryUtilities
From Version5.0.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

Use the Armis integration to search alerts and devices, tag and untag devices, and set alert statuses.
This integration was integrated and tested with the latest version of Armis.

Configure Armis in Cortex

Parameter Description Required
Server URL   True
Fetch incidents   False
Incident type   False
Maximum number of incidents per fetch   False
Fetch alerts with status (UNHANDLED, SUPPRESSED, RESOLVED)   False
Fetch alerts with type The type of alerts are Policy Violation, System Policy Violation, Anomaly Detection If no type is chosen, all types will be fetched. False
Minimum severity of alerts to fetch   True
First fetch time The date or relative timestamp from which to begin fetching alerts.

Supported formats: 2 minutes, 2 hours, 2 days, 2 weeks, 2 months, 2 years, yyyy-mm-dd, yyyy-mm-ddTHH:MM:SSZ.

For example: 01 April 2025, 01 March 2025 04:45:33, 2025-02-17T14:05:44Z.
False
Trust any certificate (not secure)   False
Secret API Key   True
Fetch Alerts AQL Use this parameter to fetch incidents using a free AQL string rather than the simpler alert type, severity, etc. False
Proxy Whether to use the System proxy 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.

armis-search-alerts


Search Armis Alerts.

Base Command

armis-search-alerts

Input

Argument Name Description Required
max_results The maximum number of results to get. Default is 50. Optional
time_frame Filter by start time.
Examples:
“3 days ago”
“1 month”
“2019-10-10T12:22:00”
“2019-10-10”. Default is 3 days.
Optional
alert_id The ID of the alert. Optional
severity A comma-separated list of alert severity levels by which to filter the search results. Possible values: “Low”, “Medium”, and “High”. Optional
status A comma-separated list of alert statuses by which to filter the search results. Possible values: “UNHANDLED”, “SUPPRESSED”, and “RESOLVED”. Optional
alert_type A comma-separated list of alert types by which to filter the search results. Possible values: “Policy Violation”, “System Policy Violation”, and “Anomaly Detection” Optional

Context Output

Path Type Description
Armis.Alert.activityIds Number The activity IDs of the alert.
Armis.Alert.activityUUIDs String The activity UUIDs of the alert.
Armis.Alert.alertId Number The ID of the alert.
Armis.Alert.connectionIds Number The connection IDs of the alert.
Armis.Alert.description String A text description of the alert.
Armis.Alert.deviceIds Number The device IDs of the alert
Armis.Alert.severity String The severity of the alert.
Armis.Alert.status String The status of the alert.
Armis.Alert.time Date The date and time the alert occurred.
Armis.Alert.title String The title of the alert.
Armis.Alert.type String The type of the alert.

Command Example

!armis-search-alerts status=RESOLVED max_results=10

Context Example

{
    "Armis": {
        "Alert": {
            "activityIds": [
                23314066,
                23316462,
                23317202,
                23326470,
                23341779,
                23342441
            ],
            "activityUUIDs": [
                "enyZFHgBAAAC-vCT9nJG",
                "0Hy2FHgBAAAC-vCTGnJB",
                "3Hy_FHgBAAAC-vCTp3Kz",
                "v3wSFXgBAAAC-vCTFnNL",
                "_nxOGHgBAAAC-vCTUnc2",
                "2HxpGHgBAAAC-vCT03jo"
            ],
            "alertId": 3984,
            "connectionIds": [
                923419,
                923501,
                924451
            ],
            "description": "Smart TV started connection to Corporate Network",
            "deviceIds": [
                165722,
                532
            ],
            "severity": "Medium",
            "status": "Resolved",
            "time": "2021-03-09T01:28:44.032944+00:00",
            "title": "Smart TV connected to Corporate network",
            "type": "System Policy Violation"
        }
    }
}

Human Readable Output

Alerts

Severity Type Time Status Title Description Activity Ids Activity UUI Ds Alert Id Connection Ids Device Ids
Medium System Policy Violation 2021-03-09T01:28:44.032944+00:00 Resolved Smart TV connected to Corporate network Smart TV started connection to Corporate Network 23314066,
23316462,
23317202,
23326470,
23341779,
23342441
enyZFHgBAAAC-vCT9nJG,
0Hy2FHgBAAAC-vCTGnJB,
3Hy_FHgBAAAC-vCTp3Kz,
v3wSFXgBAAAC-vCTFnNL,
_nxOGHgBAAAC-vCTUnc2,
2HxpGHgBAAAC-vCT03jo
3984 923419,
923501,
924451
165722,
532

armis-update-alert-status


Updates the status for an alert.

Base Command

armis-update-alert-status

Input

Argument Name Description Required
alert_id The ID of the alert to update. Required
status New status of the alert. Possible values are: UNHANDLED, RESOLVED, SUPPRESSED. Required

Context Output

There is no context output for this command.

Command Example


#### Human Readable Output

### armis-search-alerts-by-aql-string

***
Searches the alerts with a raw AQL string.

#### Base Command

`armis-search-alerts-by-aql-string`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| aql_string | The AQL string to by which to search. | Required |
| max_results | The maximum number of results to get. Default is 50. | Optional |

#### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| Armis.Alert.activityIds | Number | The activity IDs of the alert. |
| Armis.Alert.activityUUIDs | String | The activity UUIDs of the alert. |
| Armis.Alert.alertId | Number | The ID of the alert. |
| Armis.Alert.connectionIds | Number | The connection IDs of the alert. |
| Armis.Alert.description | String | The description of the alert. |
| Armis.Alert.deviceIds | Number | The device IDs of the alert. |
| Armis.Alert.severity | String | The severity of the alert. |
| Armis.Alert.status | String | The status of the alert. |
| Armis.Alert.time | Date | The date and time the alert occurred. |
| Armis.Alert.title | String | The title of the alert. |
| Armis.Alert.type | String | The type of the alert. |

#### Command Example

```!armis-search-alerts-by-aql-string aql_string="alertId:(3821)"```

#### Context Example

```json
{
    "Armis": {
        "Alert": {
            "activityIds": [
                22060159
            ],
            "activityUUIDs": [
                "nTiGqXcBAAAC-vCTfzPN"
            ],
            "alertId": 3821,
            "connectionIds": [],
            "description": "The Armis security platform has detected a violation of a policy and generated an alert.",
            "deviceIds": [
                199808
            ],
            "severity": "Medium",
            "status": "Resolved",
            "time": "2021-02-16T06:23:02.101479+00:00",
            "title": "Unencrypted Traffic: SMB",
            "type": "System Policy Violation"
        }
    }
}

Human Readable Output

Alerts

Alert Id Description Type Title Severity Status Time Activity Ids Activity UUI Ds Device Ids
3821 The Armis security platform has detected a violation of a policy and generated an alert. System Policy Violation Unencrypted Traffic: SMB Medium Resolved 2021-02-16T06:23:02.101479+00:00 22060159 nTiGqXcBAAAC-vCTfzPN 199808

armis-tag-device


Adds a tag to a device.

Base Command

armis-tag-device

Input

Argument Name Description Required
device_id The ID of the device to add a tag to. Required
tags The tags to add to the device. Required

Context Output

There is no context output for this command.

Command Example

!armis-tag-device device_id=165722 tags=test

Human Readable Output

Successfully Tagged device: 165722 with tags: [‘test’]

armis-untag-device


Removes a tag from a device.

Base Command

armis-untag-device

Input

Argument Name Description Required
device_id The ID of the device to remove a tag from. Required
tags The tags to remove from the device. Required

Context Output

There is no context output for this command.

Command Example

!armis-untag-device device_id=165722 tags=test

Human Readable Output

Successfully Untagged device: 165722 with tags: [‘test’]

armis-search-devices


Search devices by identifiers.

Base Command

armis-search-devices

Input

Argument Name Description Required
name The name of the device to search for. Optional
device_id The ID of the device to search for. Optional
mac_address The MAC address of the device to search for. Optional
ip_address The IP address of the device to search for. Optional
device_type A comma-separated list of device types by which to filter the results. for example “Routers”, “Laptops”, “IP Cameras” (there are many device types. for a full list access your Armis instance). Optional
time_frame The time frame of the device to search for. Optional
max_results The maximum number of results to get. Default is 50. Optional
risk_level A comma-separated list of device risk levels by which to filter the results. Possible values: “Low”, “Medium”, and “High”. Optional

Context Output

Path Type Description
Armis.Device.accessSwitch String The access switch of the device.
Armis.Device.category String The category of the device.
Armis.Device.firstSeen Date The first time the device was seen.
Armis.Device.id Number The ID of the device.
Armis.Device.ipaddress String The IP address of the device.
Armis.Device.ipv6 String The IPv6 address of the device.
Armis.Device.lastSeen Date The last time the device was seen.
Armis.Device.macAddress String The MAC address of the device.
Armis.Device.manufacturer String The manufacturer of the device.
Armis.Device.model String The model of the device.
Armis.Device.name String The name of the device.
Armis.Device.operatingSystem String The operating system of the device.
Armis.Device.operatingSystemVersion String The operating system version of the device.
Armis.Device.purdueLevel String The purdue level of the device.
Armis.Device.riskLevel String The risk level of the device.
Armis.Device.sensor String The sensor of the device.
Armis.Device.site String The site of the device.
Armis.Device.tags String The tags of the device.
Armis.Device.type String The type of the device.
Armis.Device.user String The user of the device.
Armis.Device.visibility String The visibility of the device.

Command example

!armis-search-devices device_id=2172

Context Example

{
    "Armis": {
        "Device": {
            "accessSwitch": null,
            "boundaries": "Corporate",
            "category": "Computers",
            "customProperties": {},
            "dataSources": [
                {
                    "firstSeen": "2022-08-11T07:54:32.167939+00:00",
                    "lastSeen": "2022-11-28T14:56:36.198248+00:00",
                    "name": "Active Directory",
                    "types": [
                        "Asset & System Management",
                        "Identity Provider"
                    ]
                },
                {
                    "firstSeen": "2021-08-15T07:37:58.891683+00:00",
                    "lastSeen": "2022-11-28T21:02:20.208248+00:00",
                    "name": "CrowdStrike",
                    "types": [
                        "Agent Based",
                        "Endpoint Protection"
                    ]
                },
                {
                    "firstSeen": "2022-07-09T07:50:51.190248+00:00",
                    "lastSeen": "2022-11-28T20:02:52.190248+00:00",
                    "name": "MBAM (BitLocker)",
                    "types": [
                        "Asset & System Management"
                    ]
                },
                {
                    "firstSeen": "2022-11-16T13:55:23.190248+00:00",
                    "lastSeen": "2022-11-28T21:30:45.190248+00:00",
                    "name": "Palo Alto Networks GlobalProtect",
                    "types": [
                        "Firewall"
                    ]
                },
                {
                    "firstSeen": "2022-07-12T12:55:47.190248+00:00",
                    "lastSeen": "2022-11-28T23:42:41.190248+00:00",
                    "name": "Qualys",
                    "types": [
                        "Vulnerability Management"
                    ]
                },
                {
                    "firstSeen": "2022-07-09T07:50:51.190248+00:00",
                    "lastSeen": "2022-11-28T20:02:52.190248+00:00",
                    "name": "SCCM",
                    "types": [
                        "Asset & System Management",
                        "Patch Management"
                    ]
                },
                {
                    "firstSeen": "2022-07-22T06:36:52.190248+00:00",
                    "lastSeen": "2022-07-22T06:40:36.190248+00:00",
                    "name": "ServiceNow",
                    "types": [
                        "Asset & System Management"
                    ]
                },
                {
                    "firstSeen": "2022-11-21T17:00:00.360310+00:00",
                    "lastSeen": "2022-11-28T22:23:35.190248+00:00",
                    "name": "Traffic Inspection",
                    "types": [
                        "Traffic Inspection",
                        "Data Analysis"
                    ]
                },
                {
                    "firstSeen": "2022-06-13T07:10:57.686241+00:00",
                    "lastSeen": "2022-11-22T00:59:54.686241+00:00",
                    "name": "User",
                    "types": [
                        "Data Upload"
                    ]
                },
                {
                    "firstSeen": "2022-11-15T10:05:08.190248+00:00",
                    "lastSeen": "2022-11-28T21:30:45.190248+00:00",
                    "name": "Aruba WLC",
                    "types": [
                        "WLC"
                    ]
                }
            ],
            "firstSeen": "2022-11-21T16:59:58.360310+00:00",
            "id": 2172,
            "ipAddress": "10.77.27.183",
            "ipv6": "fe80::647b:ba0f:9628:6014",
            "lastSeen": "2022-11-29T18:42:50.190248+00:00",
            "macAddress": "50:76:AF:D3:3F:AB",
            "manufacturer": "Lenovo",
            "model": "ThinkPad X1 Yoga 3rd Gen",
            "name": "000000731194pc.corporate.acme.com",
            "operatingSystem": "Windows",
            "operatingSystemVersion": "10",
            "purdueLevel": 4,
            "riskLevel": 5,
            "sensor": {
                "name": "PALO_ALTO-IDF04-SW01:Gig1/0/44 Enterprise",
                "type": "Access Switch"
            },
            "site": {
                "location": "Palo Alto",
                "name": "Palo Alto Enterprise"
            },
            "tags": [
                "Corporate",
                "ServiceNow",
                "SCCM"
            ],
            "type": "Laptops",
            "userIds": [
                12
            ],
            "visibility": "Full"
        }
    }
}

Human Readable Output

Devices

Risk Level Id Name Type Ip Address Ipv 6 Mac Address Operating System Operating System Version Manufacturer Model Tags
5 2172 000000731194pc.corporate.acme.com Laptops 10.77.27.183 fe80::647b:ba0f:9628:6014 50:76:AF:D3:3F:AB Windows 10 Lenovo ThinkPad X1 Yoga 3rd Gen Corporate,
ServiceNow,
SCCM

armis-search-devices-by-aql


Searches devices with a custom AQL search string.

Base Command

armis-search-devices-by-aql

Input

Argument Name Description Required
aql_string The AQL string. Required
max_results The maximum number of results to get. Default is 50. Optional

Context Output

Path Type Description
Armis.Device.accessSwitch String The access switch of the device.
Armis.Device.category String The category of the device.
Armis.Device.firstSeen Date The first time the device was seen.
Armis.Device.id Number The ID of the device.
Armis.Device.ipaddress String The IP address of the device.
Armis.Device.ipv6 String The IPv6 address of the device.
Armis.Device.lastSeen Date The last time the device was seen.
Armis.Device.macAddress String The MAC address of the device.
Armis.Device.manufacturer String The manufacturer of the device.
Armis.Device.model String The model of the device.
Armis.Device.name String The name of the device.
Armis.Device.operatingSystem String The operating system of the device.
Armis.Device.operatingSystemVersion String The operating system version of the device.
Armis.Device.purdueLevel String The purdue level of the device.
Armis.Device.riskLevel String The risk level of the device.
Armis.Device.sensor String The sensor of the device.
Armis.Device.site String The site of the device.
Armis.Device.tags String The tags of the device.
Armis.Device.type String The type of the device.
Armis.Device.user String The user of the device.
Armis.Device.visibility String The visibility of the device.

Command Example

!armis-search-devices-by-aql aql_string="macAddress:(a4:5d:36:c5:32:69)"

Context Example

{
    "Armis": {
        "Device": {
            "accessSwitch": "win-sw-hoc-01:po9",
            "category": "Computers",
            "dataSources": [
                {
                    "firstSeen": "2020-10-01T11:56:48+00:00",
                    "lastSeen": "2021-03-11T20:26:40+00:00",
                    "name": "Meraki",
                    "types": [
                        "WLC"
                    ]
                },
                {
                    "firstSeen": "2021-02-02T08:34:10.536715+00:00",
                    "lastSeen": "2021-03-11T20:21:22.374047+00:00",
                    "name": "Network Mapper",
                    "types": [
                        "Network Monitoring"
                    ]
                },
                {
                    "firstSeen": "2020-07-05T11:25:24.128383+00:00",
                    "lastSeen": "2021-03-11T20:32:33.494314+00:00",
                    "name": "Traffic Inspection",
                    "types": [
                        "Traffic Inspection",
                        "Data Analysis"
                    ]
                }
            ],
            "firstSeen": "2020-06-01T00:30:32.318087+00:00",
            "id": 74745,
            "ipAddress": "10.0.100.10",
            "ipv6": null,
            "lastSeen": "2021-03-11T20:32:33.494314+00:00",
            "macAddress": "a4:5d:36:c5:32:69",
            "manufacturer": "Hewlett Packard",
            "model": "Hewlett device",
            "name": "wc-shoretel.winslow.local",
            "operatingSystem": "Windows",
            "operatingSystemVersion": "Server 2008 R2",
            "riskLevel": 10,
            "sensor": {
                "name": "win-wap-tfm-01",
                "type": "Access Point"
            },
            "site": {
                "location": "28 Merri Concourse, Campbellfield VIc 3061 Australia",
                "name": "Winslow Small Plant"
            },
            "tags": [
                "MERAKI_NETWORK=Winslow Campbellfield"
            ],
            "type": "Servers",
            "user": "",
            "visibility": "Full"
        }
    }
}

Human Readable Output

Devices

Risk Level Name Type Ip Address Tags Id
10 wc-shoretel.winslow.local Servers 10.0.100.10 MERAKI_NETWORK=Winslow Campbellfield 74745

Configuration parameters

  • url — Server URL (required)
  • isFetch — Fetch incidents
  • incidentType — Incident type
  • incidentFetchInterval — Incidents Fetch Interval
  • max_fetch — Maximum number of incidents per fetch
  • alert_status — Fetch alerts with status (UNHANDLED,SUPPRESSED,RESOLVED)
  • alert_type — Fetch alerts with type
  • min_severity — Minimum severity of alerts to fetch (required)
  • first_fetch — First fetch time
  • insecure — Trust any certificate (not secure)
  • secret — Secret API Key (required)
  • free_fetch_string — Fetch Alerts AQL
  • proxy — Use system proxy settings

Commands (7)

  • armis-search-alerts

    Search Armis alerts.

  • armis-search-alerts-by-aql-string

    Search the alerts with a raw AQL string.

  • armis-search-devices

    Search devices by identifiers.

  • armis-search-devices-by-aql

    Search devices with a custom AQL search string.

  • armis-tag-device

    Add a tag to a device.

  • armis-untag-device

    Remove a tag from a device.

  • armis-update-alert-status

    Update the status for an alert.

"""Armis Integration for Cortex XSOAR - Unit Tests file
This file contains the Pytest Tests for the Armis Integration
"""

import json

import pytest
import time

import CommonServerPython
import demistomock as demisto


def test_untag_device_success(requests_mock):
    from Armis import Client, untag_device_command

    mock_token = {"data": {"access_token": "example", "expiration_utc": time.ctime(time.time() + 10000)}}
    requests_mock.post("https://test.com/api/v1/access_token/", json=mock_token)

    requests_mock.delete("https://test.com/api/v1/devices/1/tags/", json={})

    client = Client("secret-example", "https://test.com/api/v1", verify=False, proxy=False)
    result = untag_device_command(client, {"device_id": "1", "tags": "test-tag"})
    assert result == "Successfully Untagged device: 1 with tags: ['test-tag']"


def test_untag_device_failure(requests_mock):
    from Armis import Client, untag_device_command

    mock_token = {"data": {"access_token": "example", "expiration_utc": time.ctime(time.time() + 10000)}}
    requests_mock.post("https://test.com/api/v1/access_token/", json=mock_token)

    requests_mock.delete("https://test.com/api/v1/devices/1/tags/", json={}, status_code=400)

    client = Client("secret-example", "https://test.com/api/v1", verify=False, proxy=False)
    with pytest.raises(CommonServerPython.DemistoException):
        untag_device_command(client, {"device_id": "1", "tags": "test-tag"})


def test_tag_device(requests_mock):
    from Armis import Client, tag_device_command

    mock_token = {"data": {"access_token": "example", "expiration_utc": time.ctime(time.time() + 10000)}}
    requests_mock.post("https://test.com/api/v1/access_token/", json=mock_token)

    requests_mock.post("https://test.com/api/v1/devices/1/tags/", json={})

    client = Client("secret-example", "https://test.com/api/v1", verify=False, proxy=False)
    result = tag_device_command(client, {"device_id": "1", "tags": "test-tag"})
    assert result == "Successfully Tagged device: 1 with tags: ['test-tag']"


def test_update_alert_status(requests_mock):
    from Armis import Client, update_alert_status_command

    mock_token = {"data": {"access_token": "example", "expiration_utc": time.ctime(time.time() + 10000)}}
    requests_mock.post("https://test.com/api/v1/access_token/", json=mock_token)

    requests_mock.patch("https://test.com/api/v1/alerts/1/", json={})

    client = Client("secret-example", "https://test.com/api/v1", verify=False, proxy=False)
    args = {"alert_id": "1", "status": "UNHANDLED"}
    assert update_alert_status_command(client, args) == "Successfully Updated Alert: 1 to status: UNHANDLED"


def test_search_alerts(requests_mock):
    from Armis import Client, search_alerts_command

    mock_token = {"data": {"access_token": "example", "expiration_utc": time.ctime(time.time() + 10000)}}
    requests_mock.post("https://test.com/api/v1/access_token/", json=mock_token)

    url = "https://test.com/api/v1/search/?aql="
    url += "+".join(
        [
            "in%3Aalerts",
            "timeFrame%3A%223+days%22",
            "riskLevel%3AHigh%2CMedium",
            "status%3AUNHANDLED%2CRESOLVED",
            "type%3A%22Policy+Violation%22",
            "alertId%3A%281%29",
        ]
    )

    mock_results = {"data": {"results": []}}

    requests_mock.get(url, json=mock_results)

    client = Client("secret-example", "https://test.com/api/v1", verify=False, proxy=False)
    args = {
        "severity": "High,Medium",
        "status": "UNHANDLED,RESOLVED",
        "alert_type": "Policy Violation",
        "alert_id": "1",
        "max_results": "20",
        "time_frame": "3 days",
    }
    response = search_alerts_command(client, args)
    assert response == "No results found"

    example_alerts = [
        {
            "activityIds": [19625045, 19625223, 19625984, 19626169, 19626680, 19626818, 19628162, 19628359],
            "activityUUIDs": [
                "1-uS23YBAAAC-vCTQOhA",
                "7eut23YBAAAC-vCTkOhB",
                "Oes13HYBAAAC-vCTcel0",
                "T-tU3HYBAAAC-vCTyunu",
                "mevb3HYBAAAC-vCT9-nn",
                "uev33HYBAAAC-vCTa-mg",
                "P-u33XYBAAAC-vCTlOpq",
                "SevT3XYBAAAC-vCTA-o_",
            ],
            "alertId": 1,
            "connectionIds": [845993, 846061, 846157, 846308],
            "description": "Smart TV started connection to Corporate Network",
            "deviceIds": [165722, 532],
            "severity": "Medium",
            "status": "Unhandled",
            "time": "2021-01-07T06:39:13.320893+00:00",
            "title": "Smart TV connected to Corporate network",
            "type": "System Policy Violation",
        }
    ]
    mock_results["data"]["results"] = example_alerts

    requests_mock.get(url, json=mock_results)
    response = search_alerts_command(client, args)
    assert response.outputs == example_alerts


def test_search_alerts_by_aql(requests_mock):
    from Armis import Client, search_alerts_by_aql_command

    mock_token = {"data": {"access_token": "example", "expiration_utc": time.ctime(time.time() + 10000)}}
    requests_mock.post("https://test.com/api/v1/access_token/", json=mock_token)

    url = "https://test.com/api/v1/search/?aql="
    url += "+".join(
        [
            "in%3Aalerts",
            "timeFrame%3A%223+days%22",
            "riskLevel%3AHigh%2CMedium",
            "status%3AUNHANDLED%2CRESOLVED",
            "type%3A%22Policy+Violation%22",
        ]
    )

    mock_results = {"data": {"results": []}}

    requests_mock.get(url, json=mock_results)

    client = Client("secret-example", "https://test.com/api/v1", verify=False, proxy=False)
    args = {"aql_string": 'timeFrame:"3 days" riskLevel:High,Medium status:UNHANDLED,RESOLVED type:"Policy Violation"'}
    response = search_alerts_by_aql_command(client, args)
    assert response == "No alerts found"

    example_alerts = [
        {
            "activityIds": [19625045, 19625223, 19625984, 19626169, 19626680, 19626818, 19628162, 19628359],
            "activityUUIDs": [
                "1-uS23YBAAAC-vCTQOhA",
                "7eut23YBAAAC-vCTkOhB",
                "Oes13HYBAAAC-vCTcel0",
                "T-tU3HYBAAAC-vCTyunu",
                "mevb3HYBAAAC-vCT9-nn",
                "uev33HYBAAAC-vCTa-mg",
                "P-u33XYBAAAC-vCTlOpq",
                "SevT3XYBAAAC-vCTA-o_",
            ],
            "alertId": 1,
            "connectionIds": [845993, 846061, 846157, 846308],
            "description": "Smart TV started connection to Corporate Network",
            "deviceIds": [165722, 532],
            "severity": "Medium",
            "status": "Unhandled",
            "time": "2021-01-07T06:39:13.320893+00:00",
            "title": "Smart TV connected to Corporate network",
            "type": "System Policy Violation",
        }
    ]
    mock_results["data"]["results"] = example_alerts

    requests_mock.get(url, json=mock_results)
    response = search_alerts_by_aql_command(client, args)
    assert response.outputs == example_alerts


def test_search_devices(requests_mock):
    from Armis import Client, search_devices_command

    mock_token = {"data": {"access_token": "example", "expiration_utc": time.ctime(time.time() + 10000)}}
    requests_mock.post("https://test.com/api/v1/access_token/", json=mock_token)

    url = "https://test.com/api/v1/search/?aql=in%3Adevices+timeFrame%3A%223+days%22+deviceId%3A%281%29"
    mock_results = {"data": {"results": []}}

    requests_mock.get(url, json=mock_results)

    client = Client("secret-example", "https://test.com/api/v1", verify=False, proxy=False)
    args = {"device_id": "1", "time_frame": "3 days"}
    response = search_devices_command(client, args)
    assert response == "No devices found"

    example_alerts = [
        {
            "accessSwitch": None,
            "category": "Network Equipment",
            "dataSources": [
                {
                    "firstSeen": "2021-01-15T03:26:56+00:00",
                    "lastSeen": "2021-01-16T18:16:32+00:00",
                    "name": "Meraki",
                    "types": ["WLC"],
                }
            ],
            "firstSeen": "2021-01-15T03:26:56+00:00",
            "id": 1,
            "ipAddress": None,
            "ipv6": None,
            "lastSeen": "2021-01-16T18:16:32+00:00",
            "macAddress": "f8:ca:59:53:91:ce",
            "manufacturer": "NetComm Wireless",
            "model": "NetComm device",
            "name": "Aussie Broadband 0079",
            "operatingSystem": None,
            "operatingSystemVersion": None,
            "riskLevel": 5,
            "sensor": {"name": "win-wap-tom-Upstairs", "type": "Access Point"},
            "site": {"location": "51 Longview Court, Thomastown Vic 3074", "name": "Winslow Workshop - Thomastown"},
            "tags": ["Access Point", "Off Network", "SSID=Aussie Broadband 0079"],
            "type": "Access Point Interface",
            "user": "",
            "visibility": "Full",
        }
    ]
    mock_results["data"]["results"] = example_alerts

    requests_mock.get(url, json=mock_results)
    response = search_devices_command(client, args)
    assert response.outputs == example_alerts


def test_search_devices_by_aql(requests_mock):
    from Armis import Client, search_devices_by_aql_command

    mock_token = {"data": {"access_token": "example", "expiration_utc": time.ctime(time.time() + 10000)}}
    requests_mock.post("https://test.com/api/v1/access_token/", json=mock_token)

    url = "https://test.com/api/v1/search/?aql=in%3Adevices+timeFrame%3A%223+days%22+deviceId%3A%281%29"
    mock_results = {"data": {"results": []}}

    requests_mock.get(url, json=mock_results)

    client = Client("secret-example", "https://test.com/api/v1", verify=False, proxy=False)
    args = {"aql_string": 'timeFrame:"3 days" deviceId:(1)'}
    response = search_devices_by_aql_command(client, args)
    assert response == "No devices found"

    example_alerts = [
        {
            "accessSwitch": None,
            "category": "Network Equipment",
            "dataSources": [
                {
                    "firstSeen": "2021-01-15T03:26:56+00:00",
                    "lastSeen": "2021-01-16T18:16:32+00:00",
                    "name": "Meraki",
                    "types": ["WLC"],
                }
            ],
            "firstSeen": "2021-01-15T03:26:56+00:00",
            "id": 1,
            "ipAddress": None,
            "ipv6": None,
            "lastSeen": "2021-01-16T18:16:32+00:00",
            "macAddress": "f8:ca:59:53:91:ce",
            "manufacturer": "NetComm Wireless",
            "model": "NetComm device",
            "name": "Aussie Broadband 0079",
            "operatingSystem": None,
            "operatingSystemVersion": None,
            "riskLevel": 5,
            "sensor": {"name": "win-wap-tom-Upstairs", "type": "Access Point"},
            "site": {"location": "51 Longview Court, Thomastown Vic 3074", "name": "Winslow Workshop - Thomastown"},
            "tags": ["Access Point", "Off Network", "SSID=Aussie Broadband 0079"],
            "type": "Access Point Interface",
            "user": "",
            "visibility": "Full",
        }
    ]
    mock_results["data"]["results"] = example_alerts

    requests_mock.get(url, json=mock_results)
    response = search_devices_by_aql_command(client, args)
    assert response.outputs == example_alerts


def test_fetch_incidents_no_duplicates(mocker):
    """
    Given:
    - 'client': Armis client.
    - 'last_run': Last run parameters.

    When:
    - Performing two consecutive calls to fetch incidents

    Then:
    - Ensure incident that was already fetched is not fetched again.

    """
    from Armis import Client, fetch_incidents

    client = Client("secret-example", "https://test.com/api/v1", verify=False, proxy=False)
    last_fetch = "2021-03-09T01:00:00.000001+00:00"
    armis_incident = {"time": "2021-03-09T01:00:00.000001+00:00", "type": "System Policy Violation"}
    response = {"results": [armis_incident], "next": "more data"}
    mocker.patch.object(client, "search_alerts", return_value=response)
    next_run, incidents = fetch_incidents(client, {"last_fetch": last_fetch}, None, "Low", [], [], "", 1)
    assert next_run["last_fetch"] == last_fetch
    assert incidents[0]["rawJSON"] == json.dumps(armis_incident)
    _, incidents = fetch_incidents(client, next_run, None, "Low", [], [], "", 1)
    assert not incidents


class MockClient:
    def __init__(self, secret: str, base_url: str, verify: bool, proxy):
        pass


def test_url_parameter(mocker):
    """
    Given:
    - Instance parameters with a base URL without `api/v1` prefix.

    When:
    - Running the main function and configured the client class.

    Then:
    - Ensure that hte base URL in the client class is with teh `api/v1` prefix.

    """
    from Armis import main

    mocker.patch.object(demisto, "params", return_value={"url": "test.com"})
    mock_client = mocker.patch("Armis.Client", side_effect=MockClient)

    main()

    assert mock_client.call_args.kwargs["base_url"] == "test.com/api/v1/"


def test_get_api_token_when_found_in_integration_context(mocker):
    """Test cases for scenario when there is api_token and expiration_time in integration context."""
    from Armis import Client

    test_integration_context = {"token": "1234567890", "token_expiration": time.ctime(time.time() + 10000)}

    mocker.patch.object(demisto, "getIntegrationContext", return_value=test_integration_context)
    client = Client("secret-example", "https://test.com/api/v1", verify=False, proxy=False)

    api_token = client._get_token()

    assert api_token == test_integration_context["token"]


def test_get_api_token_when_expired_token_found_in_integration_context(mocker, requests_mock):
    """Test cases for scenario when there is an expired api_token in integration context."""
    from Armis import Client

    mock_token = {"data": {"access_token": "example", "expiration_utc": time.ctime(time.time() + 10000)}}
    requests_mock.post("https://test.com/api/v1/access_token/", json=mock_token)

    client = Client("secret-example", "https://test.com/api/v1", verify=False, proxy=False)

    api_token = client._get_token()

    assert api_token == mock_token["data"]["access_token"]


def test_retry_for_401_error(mocker, requests_mock):
    from Armis import Client, search_alerts_by_aql_command

    test_integration_context = {"token": "invalid_token", "token_expiration": time.ctime(time.time() - 10000)}

    mocker.patch.object(demisto, "getIntegrationContext", return_value=test_integration_context)

    url = "https://test.com/api/v1/search/?aql="
    url += "+".join(
        [
            "in%3Aalerts",
            "timeFrame%3A%223+days%22",
            "riskLevel%3AHigh%2CMedium",
            "status%3AUNHANDLED%2CRESOLVED",
            "type%3A%22Policy+Violation%22",
        ]
    )

    mock_results = {"message": "Invalid access token.", "success": False}

    mock_token = {"data": {"access_token": "example", "expiration_utc": time.ctime(time.time() + 10000)}}
    requests_mock.post("https://test.com/api/v1/access_token/", json=mock_token)

    example_alerts = [
        {
            "accessSwitch": None,
            "category": "Dummy Category",
            "dataSources": [
                {
                    "firstSeen": "2025-01-01T00:00:00+00:00",
                    "lastSeen": "2025-01-02T00:00:00+00:00",
                    "name": "Dummy Source",
                    "types": ["Dummy Type"],
                }
            ],
            "firstSeen": "2025-01-01T00:00:00+00:00",
            "id": 100,
            "ipAddress": "0.0.0.1",
            "ipv6": "0000:0000:0000:0000:0000:0000:0000:0001",
            "lastSeen": "2025-01-02T00:00:00+00:00",
            "macAddress": "00:00:00:00:00:01",
            "manufacturer": "Dummy Manufacturer",
            "model": "Dummy Model",
            "name": "Dummy Device",
            "operatingSystem": "Dummy OS",
            "operatingSystemVersion": "1.0",
            "riskLevel": 1,
            "sensor": {"name": "Dummy Sensor", "type": "Dummy Sensor Type"},
            "site": {"location": "Dummy Location", "name": "Dummy Site"},
            "tags": ["Dummy Tag 1", "Dummy Tag 2", "Dummy Tag 3"],
            "type": "Dummy Type",
            "user": "Dummy User",
            "visibility": "Dummy Visibility",
        }
    ]
    new_mock_results = {"data": {"results": example_alerts}}

    requests_mock.register_uri(
        "GET", url, [{"status_code": 401, "json": mock_results}, {"status_code": 200, "json": new_mock_results}]
    )

    client = Client("secret-example", "https://test.com/api/v1", verify=False, proxy=False)
    args = {"aql_string": 'timeFrame:"3 days" riskLevel:High,Medium status:UNHANDLED,RESOLVED type:"Policy Violation"'}

    response = search_alerts_by_aql_command(client, args)
    assert response.outputs == example_alerts


def test_test_module_when_is_fetch_is_true(mocker):
    """
    Given:
    - 'client': Armis client.
    - 'params': A dictionary containing the parameters provided by the user.

    When:
    - Performing calls to test_module

    Then:
    - Ensure test_module returns 'ok'

    """
    from Armis import Client, test_module as armis_test_module

    params = {
        "isFetch": True,
        "min_severity": "Low",
        "alert_type": [],
        "alert_status": [],
        "free_fetch_string": "",
        "first_fetch": "3 days",
        "max_fetch": 10,
    }

    mocker.patch.object(demisto, "params", return_value=params)

    client = Client("secret-example", "https://test.com/api/v1", verify=False, proxy=False)

    armis_incident = {"time": "2025-03-09T01:00:00.000001+00:00", "type": "test_type"}
    response = {"results": [armis_incident], "next": "more data"}
    mocker.patch.object(client, "search_alerts", return_value=response)

    assert armis_test_module(client, params) == "ok"