Axonius

This integration is for fetching information about assets in Axonius.

Data Enrichment & Threat Intelligence · Axonius

Details

IDAxonius
ProviderAxonius
CategoryData Enrichment & Threat Intelligence
From Version5.0.0
Docker Imagedemisto/axonius:1.1.0.8767810
Supported ModulesAgentix XSIAM

README

This integration is for fetching information about assets in Axonius.
This integration was integrated and tested with version 3.9 of Axonius

Configure Axonius in Cortex

Parameter Required
Server URL (e.g. https://example.net) True
Axonius API Key True
Axonius API Secret True
Trust any certificate (not secure) False
Use system proxy settings 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.

axonius-get-devices-by-savedquery


Gather device info by saved query

Base Command

axonius-get-devices-by-savedquery

Input

Argument Name Description Required
saved_query_name The name of the devices saved query within Axonius. See https://docs.axonius.com/docs/saved-queries-devices. Required
max_results The maximum number of results to return. Default is 50. Optional
fields Comma separated list of Axonius fields to return. Optional

Context Output

Path Type Description
Axonius.Devices.adapter_list_length Number The number of adapters with information about the asset.
Axonius.Devices.adapters String The specific adapter names with asset information.
Axonius.Devices.internal_axon_id String The internal unique Axonius identifier for the asset.
Axonius.Devices.hostname String The hostnames of the assset.
Axonius.Devices.name String The names of the asset.
Axonius.Devices.last_seen Date Last seen date/time of the asset.
Axonius.Devices.network_interfaces_macs String The MAC addresses of the asset.
Axonius.Devices.network_interfaces_ips String The IP addresses of the asset.
Axonius.Devices.os_type String The OS type (Windows, Linux, macOS,…).
Axonius.Devices.labels String Tags assigned to the asset.

Command Example

!axonius-get-devices-by-savedquery saved_query_name=example_query

Context Example

{
    "Axonius": {
        "Devices": {
            "adapter_list_length": 5,
            "adapters": [
                "nexpose_adapter",
                "esx_adapter",
                "active_directory_adapter",
                "solarwinds_orion_adapter",
                "crowd_strike_adapter",
                "esx_adapter",
                "crowd_strike_adapter",
                "crowd_strike_adapter",
                "crowd_strike_adapter",
                "esx_adapter"
            ],
            "aggregated_hostname": [
                "DC4"
            ],
            "aggregated_last_seen": "2020-09-08T06:44:31+00:00",
            "aggregated_name": [
                "Windows%20Server%202012%20r2%20dc4.TestDomain.test%20(Avidor)",
                "DC4",
                "Windows Server 2012 R2",
                "Windows Server - 2012 - R2"
            ],
            "aggregated_network_interfaces_ips": [
                "x.x.x.x",
            ],
            "aggregated_network_interfaces_mac": [
                "00:0C:29:B6:DA:46",
                "00:50:56:91:DE:BB",
                "00:50:56:91:3A:EC",
                "00:50:56:91:33:E2",
                "00:50:56:91:21:B3"
            ],
            "aggregated_os_type": [
                "Windows"
            ],
            "internal_axon_id": "d530db3cfef6a2220b315d54fa1901b2"
        }
    }
}

Human Readable Output

Results

adapter_list_length adapters aggregated_hostname aggregated_last_seen aggregated_name aggregated_network_interfaces_ips aggregated_network_interfaces_mac aggregated_os_type internal_axon_id
5 nexpose_adapter,
esx_adapter,
active_directory_adapter,
solarwinds_orion_adapter,
crowd_strike_adapter,
esx_adapter,
crowd_strike_adapter,
crowd_strike_adapter,
crowd_strike_adapter,
esx_adapter
DC4 2020-09-08T06:44:31+00:00 Windows%20Server%202012%20r2%20dc4.TestDomain.test%20(Avidor),
DC4,
Windows Server 2012 R2,
Windows Server - 2012 - R2
192.168.20.17,
192.168.20.58,
fe80::2dba:9118:1fc8:7759,
192.168.20.36,
192.168.20.50,
192.168.20.61
00:0C:29:B6:DA:46,
00:50:56:91:DE:BB,
00:50:56:91:3A:EC,
00:50:56:91:33:E2,
00:50:56:91:21:B3
Windows d530db3cfef6a2220b315d54fa1901b2

axonius-get-devices-by-aql


Gather devices info by AQL query

Base Command

axonius-get-devices-by-aql

Input

Argument Name Description Required
query The AQL query to filter devices by. Required
max_results The maximum number of results to return. Default is 50. Optional
fields Comma separated list of Axonius fields to return. Optional

Context Output

Path Type Description
Axonius.Devices.adapter_list_length Number The number of adapters with information about the asset.
Axonius.Devices.adapters String The specific adapter names with asset information.
Axonius.Devices.internal_axon_id String The internal unique Axonius identifier for the asset.
Axonius.Devices.hostname String The hostnames of the assset.
Axonius.Devices.name String The names of the asset.
Axonius.Devices.last_seen Date Last seen date/time of the asset.
Axonius.Devices.network_interfaces_macs String The MAC addresses of the asset.
Axonius.Devices.network_interfaces_ips String The IP addresses of the asset.
Axonius.Devices.os_type String The OS type (Windows, Linux, macOS,…).
Axonius.Devices.labels String Tags assigned to the asset.

Command Example

!axonius-get-devices-by-aql query="((\"specific_data.data.name\" == ({\"$exists\":true,\"$ne\":\"\"})))" max_results="50"

Context Example

{
    "Axonius": {
        "Devices": {
            "adapter_list_length": 5,
            "adapters": [
                "nexpose_adapter",
                "esx_adapter",
                "active_directory_adapter",
                "solarwinds_orion_adapter",
                "crowd_strike_adapter",
                "esx_adapter",
                "crowd_strike_adapter",
                "crowd_strike_adapter",
                "crowd_strike_adapter",
                "esx_adapter"
            ],
            "aggregated_hostname": [
                "DC4"
            ],
            "aggregated_last_seen": "2020-09-08T06:44:31+00:00",
            "aggregated_name": [
                "Windows%20Server%202012%20r2%20dc4.TestDomain.test%20(Avidor)",
                "DC4",
                "Windows Server 2012 R2",
                "Windows Server - 2012 - R2"
            ],
            "aggregated_network_interfaces_ips": [
                "x.x.x.x",
            ],
            "aggregated_network_interfaces_mac": [
                "00:0C:29:B6:DA:46",
                "00:50:56:91:DE:BB",
                "00:50:56:91:3A:EC",
                "00:50:56:91:33:E2",
                "00:50:56:91:21:B3"
            ],
            "aggregated_os_type": [
                "Windows"
            ],
            "internal_axon_id": "d530db3cfef6a2220b315d54fa1901b2"
        }
    }
}

Human Readable Output

Results

adapter_list_length adapters aggregated_hostname aggregated_last_seen aggregated_name aggregated_network_interfaces_ips aggregated_network_interfaces_mac aggregated_os_type internal_axon_id
5 nexpose_adapter,
esx_adapter,
active_directory_adapter,
solarwinds_orion_adapter,
crowd_strike_adapter,
esx_adapter,
crowd_strike_adapter,
crowd_strike_adapter,
crowd_strike_adapter,
esx_adapter
DC4 2020-09-08T06:44:31+00:00 Windows%20Server%202012%20r2%20dc4.TestDomain.test%20(Avidor),
DC4,
Windows Server 2012 R2,
Windows Server - 2012 - R2
192.168.20.17,
192.168.20.58,
fe80::2dba:9118:1fc8:7759,
192.168.20.36,
192.168.20.50,
192.168.20.61
00:0C:29:B6:DA:46,
00:50:56:91:DE:BB,
00:50:56:91:3A:EC,
00:50:56:91:33:E2,
00:50:56:91:21:B3
Windows d530db3cfef6a2220b315d54fa1901b2

axonius-get-users-by-aql


Gather users info by AQL query

Base Command

axonius-get-users-by-aql

Input

Argument Name Description Required
query The AQL query to filter users by. Required
max_results The maximum number of results to return. Default is 50. Optional
fields Comma separated list of Axonius fields to return. Optional

Context Output

Path Type Description
Axonius.Users.adapter_list_length Number The number of adapters with information about the asset.
Axonius.Users.adapters String The specific adapter names with asset information.
Axonius.Users.internal_axon_id String The internal unique Axonius identifier for the asset.
Axonius.Users.username String Username of the asset.
Axonius.Users.mail String Email address of the asset.
Axonius.Users.is_admin Boolean If the asset has admin privileges.
Axonius.Users.last_seen Date Last seen date/time of the asset.
Axonius.Users.labels String Tags assigned to the asset.

Command Example

!axonius-get-users-by-aql query="((\"specific_data.data.username\" == ({\"$exists\":true,\"$ne\":\"\"})))" max_results="50"

Context Example

{
    "Axonius": {
        "Users": {
            "adapter_list_length": 1,
            "adapters": [
                "active_directory_adapter"
            ],
            "aggregated_domain": "TestDomain.test",
            "aggregated_is_admin": false,
            "aggregated_last_seen": "2018-11-01T14:48:59+00:00",
            "aggregated_username": "test_ldap_login_user",
            "internal_axon_id": "4d5f47f067388e8ffc53b6bbe8a10800"
        }
    }
}

Human Readable Output

Results

adapter_list_length adapters aggregated_domain aggregated_is_admin aggregated_last_seen aggregated_username internal_axon_id
1 active_directory_adapter TestDomain.test false 2018-11-01T14:48:59+00:00 test_ldap_login_user 4d5f47f067388e8ffc53b6bbe8a10800

axonius-get-users-by-savedquery


Gather user info by saved query

Base Command

axonius-get-users-by-savedquery

Input

Argument Name Description Required
saved_query_name The name of the users saved query within Axonius. See https://docs.axonius.com/docs/saved-queries-users. Required
max_results The maximum number of results to return. Default is 50. Optional
fields Comma separated list of Axonius fields to return. Optional

Context Output

Path Type Description
Axonius.Users.adapter_list_length Number The number of adapters with information about the asset.
Axonius.Users.adapters String The specific adapter names with asset information.
Axonius.Users.internal_axon_id String The internal unique Axonius identifier for the asset.
Axonius.Users.username String Username of the asset.
Axonius.Users.mail String Email address of the asset.
Axonius.Users.is_admin Boolean If the asset has admin privileges.
Axonius.Users.last_seen Date Last seen date/time of the asset.
Axonius.Users.labels String Tags assigned to the asset.

Command Example

!axonius-get-users-by-savedquery saved_query_name=example_query

Context Example

{
    "Axonius": {
        "Users": {
            "adapter_list_length": 1,
            "adapters": [
                "active_directory_adapter"
            ],
            "aggregated_domain": "TestDomain.test",
            "aggregated_is_admin": false,
            "aggregated_last_seen": "2018-11-01T14:48:59+00:00",
            "aggregated_username": "test_ldap_login_user",
            "internal_axon_id": "4d5f47f067388e8ffc53b6bbe8a10800"
        }
    }
}

Human Readable Output

Results

adapter_list_length adapters aggregated_domain aggregated_is_admin aggregated_last_seen aggregated_username internal_axon_id
1 active_directory_adapter TestDomain.test false 2018-11-01T14:48:59+00:00 test_ldap_login_user 4d5f47f067388e8ffc53b6bbe8a10800

axonius-get-users-by-mail


Gather user info by email address

Base Command

axonius-get-users-by-mail

Input

Argument Name Description Required
value The user email address to search for within Axonius. Required
max_results The maximum number of results to return. Default is 50. Optional
fields Comma separated list of Axonius fields to return. Optional

Context Output

Path Type Description
Axonius.Users.adapter_list_length Number The number of adapters with information about the asset.
Axonius.Users.adapters String The specific adapter names with asset information.
Axonius.Users.internal_axon_id String The internal unique Axonius identifier for the asset.
Axonius.Users.username String Username of the asset.
Axonius.Users.mail String Email address of the asset.
Axonius.Users.is_admin Boolean If the asset has admin privileges.
Axonius.Users.last_seen Date Last seen date/time of the asset.
Axonius.Users.labels String Tags assigned to the asset.

Command Example

!axonius-get-users-by-mail value=Administrator@testdomain.test

Context Example

{
    "Axonius": {
        "Users": {
            "adapter_list_length": 1,
            "adapters": [
                "active_directory_adapter"
            ],
            "aggregated_mail": [
                "Administrator@testdomain.test"
            ],
            "aggregated_username": [
                "Administrator"
            ],
            "internal_axon_id": "a6f0d051a30d401b7f73416fbc90a3cf"
        }
    }
}

Human Readable Output

Results

adapter_list_length adapters aggregated_mail aggregated_username internal_axon_id
1 active_directory_adapter Administrator@testdomain.test Administrator a6f0d051a30d401b7f73416fbc90a3cf

axonius-get-users-by-mail-regex


Gather user info by email address using regex

Base Command

axonius-get-users-by-mail-regex

Input

Argument Name Description Required
value The user email address to search for within Axonius. Required
max_results The maximum number of results to return. Default is 50. Optional
fields Comma separated list of Axonius fields to return. Optional

Context Output

Path Type Description
Axonius.Users.adapter_list_length Number The number of adapters with information about the asset.
Axonius.Users.adapters String The specific adapter names with asset information.
Axonius.Users.internal_axon_id String The internal unique Axonius identifier for the asset.
Axonius.Users.username String Username of the asset.
Axonius.Users.mail String Email address of the asset.
Axonius.Users.is_admin Boolean If the asset has admin privileges.
Axonius.Users.last_seen Date Last seen date/time of the asset.
Axonius.Users.labels String Tags assigned to the asset.

Command Example

!axonius-get-users-by-mail-regex value=Administrator

Context Example

{
    "Axonius": {
        "Users": {
            "adapter_list_length": 1,
            "adapters": [
                "active_directory_adapter"
            ],
            "aggregated_mail": [
                "Administrator@testdomain.test"
            ],
            "aggregated_username": [
                "Administrator"
            ],
            "internal_axon_id": "a6f0d051a30d401b7f73416fbc90a3cf"
        }
    }
}

Human Readable Output

Results

adapter_list_length adapters aggregated_mail aggregated_username internal_axon_id
1 active_directory_adapter Administrator@testdomain.test Administrator a6f0d051a30d401b7f73416fbc90a3cf

axonius-get-users-by-username


Gather user info by username

Base Command

axonius-get-users-by-username

Input

Argument Name Description Required
value The username to search for within Axonius. Required
max_results The maximum number of results to return. Default is 50. Optional
fields Comma separated list of Axonius fields to return. Optional

Context Output

Path Type Description
Axonius.Users.adapter_list_length Number The number of adapters with information about the asset.
Axonius.Users.adapters String The specific adapter names with asset information.
Axonius.Users.internal_axon_id String The internal unique Axonius identifier for the asset.
Axonius.Users.username String Username of the asset.
Axonius.Users.mail String Email address of the asset.
Axonius.Users.is_admin Boolean If the asset has admin privileges.
Axonius.Users.last_seen Date Last seen date/time of the asset.
Axonius.Users.labels String Tags assigned to the asset.

Command Example

!axonius-get-users-by-username value=test_ldap_login_user

Context Example

{
    "Axonius": {
        "Users": {
            "adapter_list_length": 1,
            "adapters": [
                "active_directory_adapter"
            ],
            "aggregated_username": "test_ldap_login_user",
            "internal_axon_id": "4d5f47f067388e8ffc53b6bbe8a10800"
        }
    }
}

Human Readable Output

Results

adapter_list_length adapters aggregated_username internal_axon_id
1 active_directory_adapter test_ldap_login_user 4d5f47f067388e8ffc53b6bbe8a10800

axonius-get-users-by-username-regex


Gather user info by username using regex

Base Command

axonius-get-users-by-username-regex

Input

Argument Name Description Required
value The username to search for within Axonius. Required
max_results The maximum number of results to return. Default is 50. Optional
fields Comma separated list of Axonius fields to return. Optional

Context Output

Path Type Description
Axonius.Users.adapter_list_length Number The number of adapters with information about the asset.
Axonius.Users.adapters String The specific adapter names with asset information.
Axonius.Users.internal_axon_id String The internal unique Axonius identifier for the asset.
Axonius.Users.username String Username of the asset.
Axonius.Users.mail String Email address of the asset.
Axonius.Users.is_admin Boolean If the asset has admin privileges.
Axonius.Users.last_seen Date Last seen date/time of the asset.
Axonius.Users.labels String Tags assigned to the asset.

Command Example

!axonius-get-users-by-username-regex value=test

Context Example

{
    "Axonius": {
        "Users": {
            "adapter_list_length": 1,
            "adapters": [
                "active_directory_adapter"
            ],
            "aggregated_username": "test_ldap_login_user",
            "internal_axon_id": "4d5f47f067388e8ffc53b6bbe8a10800"
        }
    }
}

Human Readable Output

Results

adapter_list_length adapters aggregated_username internal_axon_id
1 active_directory_adapter test_ldap_login_user 4d5f47f067388e8ffc53b6bbe8a10800

axonius-get-devices-by-hostname


Gather device info by hostname

Base Command

axonius-get-devices-by-hostname

Input

Argument Name Description Required
value The hostname to search for within Axonius. Required
max_results The maximum number of results to return. Default is 50. Optional
fields Comma separated list of Axonius fields to return. Optional

Context Output

Path Type Description
Axonius.Devices.adapter_list_length Number The number of adapters with information about the asset.
Axonius.Devices.adapters String The specific adapter names with asset information.
Axonius.Devices.internal_axon_id String The internal unique Axonius identifier for the asset.
Axonius.Devices.hostname String The hostnames of the assset.
Axonius.Devices.name String The names of the asset.
Axonius.Devices.last_seen Date Last seen date/time of the asset.
Axonius.Devices.network_interfaces_macs String The MAC addresses of the asset.
Axonius.Devices.network_interfaces_ips String The IP addresses of the asset.
Axonius.Devices.os_type String The OS type (Windows, Linux, macOS,…).
Axonius.Devices.labels String Tags assigned to the asset.

Command Example

!axonius-get-devices-by-hostname value=DC4

Context Example

{
    "Axonius": {
        "Devices": {
            "adapter_list_length": 5,
            "adapters": [
                "nexpose_adapter",
                "esx_adapter",
                "active_directory_adapter",
                "solarwinds_orion_adapter",
                "crowd_strike_adapter",
                "esx_adapter",
                "crowd_strike_adapter",
                "crowd_strike_adapter",
                "crowd_strike_adapter",
                "esx_adapter"
            ],
            "aggregated_hostname": [
                "DC4"
            ],
            "aggregated_network_interfaces_ips": [
                "x.x.x.x",
            ],
            "aggregated_network_interfaces_mac": [
                "00:0C:29:B6:DA:46",
                "00:50:56:91:DE:BB",
                "00:50:56:91:3A:EC",
                "00:50:56:91:33:E2",
                "00:50:56:91:21:B3"
            ],
            "aggregated_network_interfaces_subnets": [
                "x.x.x.x/24"
            ],
            "internal_axon_id": "d530db3cfef6a2220b315d54fa1901b2"
        }
    }
}

Human Readable Output

Results

adapter_list_length adapters aggregated_hostname aggregated_network_interfaces_ips aggregated_network_interfaces_mac aggregated_network_interfaces_subnets internal_axon_id
5 nexpose_adapter,
esx_adapter,
active_directory_adapter,
solarwinds_orion_adapter,
crowd_strike_adapter,
esx_adapter,
crowd_strike_adapter,
crowd_strike_adapter,
crowd_strike_adapter,
esx_adapter
DC4 192.168.20.17,
192.168.20.58,
fe80::2dba:9118:1fc8:7759,
192.168.20.36,
192.168.20.50,
192.168.20.61
00:0C:29:B6:DA:46,
00:50:56:91:DE:BB,
00:50:56:91:3A:EC,
00:50:56:91:33:E2,
00:50:56:91:21:B3
x.x.x.x/24 d530db3cfef6a2220b315d54fa1901b2

axonius-get-devices-by-hostname-regex


Gather device info by hostname using regex

Base Command

axonius-get-devices-by-hostname-regex

Input

Argument Name Description Required
value The hostname to search for within Axonius using regex. Required
max_results The maximum number of results to return. Default is 50. Optional
fields Comma separated list of Axonius fields to return. Optional

Context Output

Path Type Description
Axonius.Devices.adapter_list_length Number The number of adapters with information about the asset.
Axonius.Devices.adapters String The specific adapter names with asset information.
Axonius.Devices.internal_axon_id String The internal unique Axonius identifier for the asset.
Axonius.Devices.hostname String The hostnames of the assset.
Axonius.Devices.name String The names of the asset.
Axonius.Devices.last_seen Date Last seen date/time of the asset.
Axonius.Devices.network_interfaces_macs String The MAC addresses of the asset.
Axonius.Devices.network_interfaces_ips String The IP addresses of the asset.
Axonius.Devices.os_type String The OS type (Windows, Linux, macOS,…).
Axonius.Devices.labels String Tags assigned to the asset.

Command Example

!axonius-get-devices-by-hostname-regex value=DC4

Context Example

{
    "Axonius": {
        "Devices": {
            "adapter_list_length": 5,
            "adapters": [
                "nexpose_adapter",
                "esx_adapter",
                "active_directory_adapter",
                "solarwinds_orion_adapter",
                "crowd_strike_adapter",
                "esx_adapter",
                "crowd_strike_adapter",
                "crowd_strike_adapter",
                "crowd_strike_adapter",
                "esx_adapter"
            ],
            "aggregated_hostname": [
                "DC4"
            ],
            "aggregated_network_interfaces_ips": [
                "x.x.x.x",
            ],
            "aggregated_network_interfaces_mac": [
                "00:0C:29:B6:DA:46",
                "00:50:56:91:DE:BB",
                "00:50:56:91:3A:EC",
                "00:50:56:91:33:E2",
                "00:50:56:91:21:B3"
            ],
            "aggregated_network_interfaces_subnets": [
                "x.x.x.x/24"
            ],
            "internal_axon_id": "d530db3cfef6a2220b315d54fa1901b2"
        }
    }
}

Human Readable Output

Results

adapter_list_length adapters aggregated_hostname aggregated_network_interfaces_ips aggregated_network_interfaces_mac aggregated_network_interfaces_subnets internal_axon_id
5 nexpose_adapter,
esx_adapter,
active_directory_adapter,
solarwinds_orion_adapter,
crowd_strike_adapter,
esx_adapter,
crowd_strike_adapter,
crowd_strike_adapter,
crowd_strike_adapter,
esx_adapter
DC4 192.168.20.17,
192.168.20.58,
fe80::2dba:9118:1fc8:7759,
192.168.20.36,
192.168.20.50,
192.168.20.61
00:0C:29:B6:DA:46,
00:50:56:91:DE:BB,
00:50:56:91:3A:EC,
00:50:56:91:33:E2,
00:50:56:91:21:B3
x.x.x.x/24 d530db3cfef6a2220b315d54fa1901b2

axonius-get-devices-by-ip


Gather device info by IP address

Base Command

axonius-get-devices-by-ip

Input

Argument Name Description Required
value The IP address to search for within Axonius. Required
max_results The maximum number of results to return. Default is 50. Optional
fields Comma separated list of Axonius fields to return. Optional

Context Output

Path Type Description
Axonius.Devices.adapter_list_length Number The number of adapters with information about the asset.
Axonius.Devices.adapters String The specific adapter names with asset information.
Axonius.Devices.internal_axon_id String The internal unique Axonius identifier for the asset.
Axonius.Devices.hostname String The hostnames of the assset.
Axonius.Devices.name String The names of the asset.
Axonius.Devices.last_seen Date Last seen date/time of the asset.
Axonius.Devices.network_interfaces_macs String The MAC addresses of the asset.
Axonius.Devices.network_interfaces_ips String The IP addresses of the asset.
Axonius.Devices.os_type String The OS type (Windows, Linux, macOS,…).
Axonius.Devices.labels String Tags assigned to the asset.

Command Example

!axonius-get-devices-by-ip value=192.168.20.17

Context Example

{
    "Axonius": {
        "Devices": {
            "adapter_list_length": 5,
            "adapters": [
                "nexpose_adapter",
                "esx_adapter",
                "active_directory_adapter",
                "solarwinds_orion_adapter",
                "crowd_strike_adapter",
                "esx_adapter",
                "crowd_strike_adapter",
                "crowd_strike_adapter",
                "crowd_strike_adapter",
                "esx_adapter"
            ],
            "aggregated_hostname": [
                "DC4"
            ],
            "aggregated_network_interfaces_ips": [
                "x.x.x.x",
            ],
            "aggregated_network_interfaces_mac": [
                "00:0C:29:B6:DA:46",
                "00:50:56:91:DE:BB",
                "00:50:56:91:3A:EC",
                "00:50:56:91:33:E2",
                "00:50:56:91:21:B3"
            ],
            "aggregated_network_interfaces_subnets": [
                "x.x.x.x/24"
            ],
            "internal_axon_id": "d530db3cfef6a2220b315d54fa1901b2"
        }
    }
}

Human Readable Output

Results

adapter_list_length adapters aggregated_hostname aggregated_network_interfaces_ips aggregated_network_interfaces_mac aggregated_network_interfaces_subnets internal_axon_id
5 nexpose_adapter,
esx_adapter,
active_directory_adapter,
solarwinds_orion_adapter,
crowd_strike_adapter,
esx_adapter,
crowd_strike_adapter,
crowd_strike_adapter,
crowd_strike_adapter,
esx_adapter
DC4 192.168.20.17,
192.168.20.58,
fe80::2dba:9118:1fc8:7759,
192.168.20.36,
192.168.20.50,
192.168.20.61
00:0C:29:B6:DA:46,
00:50:56:91:DE:BB,
00:50:56:91:3A:EC,
00:50:56:91:33:E2,
00:50:56:91:21:B3
x.x.x.x/24 d530db3cfef6a2220b315d54fa1901b2

axonius-get-devices-by-ip-regex


Gather device info by IP address using regex

Base Command

axonius-get-devices-by-ip-regex

Input

Argument Name Description Required
value The IP address to search for within Axonius. Required
max_results The maximum number of results to return. Default is 50. Optional
fields Comma separated list of Axonius fields to return. Optional

Context Output

Path Type Description
Axonius.Devices.adapter_list_length Number The number of adapters with information about the asset.
Axonius.Devices.adapters String The specific adapter names with asset information.
Axonius.Devices.internal_axon_id String The internal unique Axonius identifier for the asset.
Axonius.Devices.hostname String The hostnames of the assset.
Axonius.Devices.name String The names of the asset.
Axonius.Devices.last_seen Date Last seen date/time of the asset.
Axonius.Devices.network_interfaces_macs String The MAC addresses of the asset.
Axonius.Devices.network_interfaces_ips String The IP addresses of the asset.
Axonius.Devices.os_type String The OS type (Windows, Linux, macOS,…).
Axonius.Devices.labels String Tags assigned to the asset.

Command Example

!axonius-get-devices-by-ip-regex value=192.168

Context Example

{
    "Axonius": {
        "Devices": {
            "adapter_list_length": 5,
            "adapters": [
                "nexpose_adapter",
                "esx_adapter",
                "active_directory_adapter",
                "solarwinds_orion_adapter",
                "crowd_strike_adapter",
                "esx_adapter",
                "crowd_strike_adapter",
                "crowd_strike_adapter",
                "crowd_strike_adapter",
                "esx_adapter"
            ],
            "aggregated_hostname": [
                "DC4"
            ],
            "aggregated_network_interfaces_ips": [
                "x.x.x.x",
            ],
            "aggregated_network_interfaces_mac": [
                "00:0C:29:B6:DA:46",
                "00:50:56:91:DE:BB",
                "00:50:56:91:3A:EC",
                "00:50:56:91:33:E2",
                "00:50:56:91:21:B3"
            ],
            "aggregated_network_interfaces_subnets": [
                "x.x.x.x/24"
            ],
            "internal_axon_id": "d530db3cfef6a2220b315d54fa1901b2"
        }
    }
}

Human Readable Output

Results

adapter_list_length adapters aggregated_hostname aggregated_network_interfaces_ips aggregated_network_interfaces_mac aggregated_network_interfaces_subnets internal_axon_id
5 nexpose_adapter,
esx_adapter,
active_directory_adapter,
solarwinds_orion_adapter,
crowd_strike_adapter,
esx_adapter,
crowd_strike_adapter,
crowd_strike_adapter,
crowd_strike_adapter,
esx_adapter
DC4 192.168.20.17,
192.168.20.58,
fe80::2dba:9118:1fc8:7759,
192.168.20.36,
192.168.20.50,
192.168.20.61
00:0C:29:B6:DA:46,
00:50:56:91:DE:BB,
00:50:56:91:3A:EC,
00:50:56:91:33:E2,
00:50:56:91:21:B3
x.x.x.x/24 d530db3cfef6a2220b315d54fa1901b2

axonius-get-devices-by-mac


Gather device info by MAC address

Base Command

axonius-get-devices-by-mac

Input

Argument Name Description Required
value The MAC address to search for within Axonius. Required
max_results The maximum number of results to return. Default is 50. Optional
fields Comma separated list of Axonius fields to return. Optional

Context Output

Path Type Description
Axonius.Devices.adapter_list_length Number The number of adapters with information about the asset.
Axonius.Devices.adapters String The specific adapter names with asset information.
Axonius.Devices.internal_axon_id String The internal unique Axonius identifier for the asset.
Axonius.Devices.hostname String The hostnames of the assset.
Axonius.Devices.name String The names of the asset.
Axonius.Devices.last_seen Date Last seen date/time of the asset.
Axonius.Devices.network_interfaces_macs String The MAC addresses of the asset.
Axonius.Devices.network_interfaces_ips String The IP addresses of the asset.
Axonius.Devices.os_type String The OS type (Windows, Linux, macOS,…).
Axonius.Devices.labels String Tags assigned to the asset.

Command Example

!axonius-get-devices-by-mac value=00:0C:29:B6:DA:46

Context Example

{
    "Axonius": {
        "Devices": {
            "adapter_list_length": 5,
            "adapters": [
                "nexpose_adapter",
                "esx_adapter",
                "active_directory_adapter",
                "solarwinds_orion_adapter",
                "crowd_strike_adapter",
                "esx_adapter",
                "crowd_strike_adapter",
                "crowd_strike_adapter",
                "crowd_strike_adapter",
                "esx_adapter"
            ],
            "aggregated_hostname": [
                "DC4"
            ],
            "aggregated_network_interfaces_ips": [
                "x.x.x.x",
            ],
            "aggregated_network_interfaces_mac": [
                "00:0C:29:B6:DA:46",
                "00:50:56:91:DE:BB",
                "00:50:56:91:3A:EC",
                "00:50:56:91:33:E2",
                "00:50:56:91:21:B3"
            ],
            "aggregated_network_interfaces_subnets": [
                "x.x.x.x/24"
            ],
            "internal_axon_id": "d530db3cfef6a2220b315d54fa1901b2"
        }
    }
}

Human Readable Output

Results

adapter_list_length adapters aggregated_hostname aggregated_network_interfaces_ips aggregated_network_interfaces_mac aggregated_network_interfaces_subnets internal_axon_id
5 nexpose_adapter,
esx_adapter,
active_directory_adapter,
solarwinds_orion_adapter,
crowd_strike_adapter,
esx_adapter,
crowd_strike_adapter,
crowd_strike_adapter,
crowd_strike_adapter,
esx_adapter
DC4 192.168.20.17,
192.168.20.58,
fe80::2dba:9118:1fc8:7759,
192.168.20.36,
192.168.20.50,
192.168.20.61
00:0C:29:B6:DA:46,
00:50:56:91:DE:BB,
00:50:56:91:3A:EC,
00:50:56:91:33:E2,
00:50:56:91:21:B3
x.x.x.x/24 d530db3cfef6a2220b315d54fa1901b2

axonius-get-devices-by-mac-regex


Gather device info by MAC address using regex

Base Command

axonius-get-devices-by-mac-regex

Input

Argument Name Description Required
value The MAC address to search for within Axonius. Required
max_results The maximum number of results to return. Default is 50. Optional
fields Comma separated list of Axonius fields to return. Optional

Context Output

Path Type Description
Axonius.Devices.adapter_list_length Number The number of adapters with information about the asset.
Axonius.Devices.adapters String The specific adapter names with asset information.
Axonius.Devices.internal_axon_id String The internal unique Axonius identifier for the asset.
Axonius.Devices.hostname String The hostnames of the assset.
Axonius.Devices.name String The names of the asset.
Axonius.Devices.last_seen Date Last seen date/time of the asset.
Axonius.Devices.network_interfaces_macs String The MAC addresses of the asset.
Axonius.Devices.network_interfaces_ips String The IP addresses of the asset.
Axonius.Devices.os_type String The OS type (Windows, Linux, macOS,…).
Axonius.Devices.labels String Tags assigned to the asset.

Command Example

!axonius-get-devices-by-mac-regex value=DA:46

Context Example

{
    "Axonius": {
        "Devices": {
            "adapter_list_length": 5,
            "adapters": [
                "nexpose_adapter",
                "esx_adapter",
                "active_directory_adapter",
                "solarwinds_orion_adapter",
                "crowd_strike_adapter",
                "esx_adapter",
                "crowd_strike_adapter",
                "crowd_strike_adapter",
                "crowd_strike_adapter",
                "esx_adapter"
            ],
            "aggregated_hostname": [
                "DC4"
            ],
            "aggregated_network_interfaces_ips": [
                "x.x.x.x",
            ],
            "aggregated_network_interfaces_mac": [
                "00:0C:29:B6:DA:46",
                "00:50:56:91:DE:BB",
                "00:50:56:91:3A:EC",
                "00:50:56:91:33:E2",
                "00:50:56:91:21:B3"
            ],
            "aggregated_network_interfaces_subnets": [
                "x.x.x.x/24"
            ],
            "internal_axon_id": "d530db3cfef6a2220b315d54fa1901b2"
        }
    }
}

Human Readable Output

Results

adapter_list_length adapters aggregated_hostname aggregated_network_interfaces_ips aggregated_network_interfaces_mac aggregated_network_interfaces_subnets internal_axon_id
5 nexpose_adapter,
esx_adapter,
active_directory_adapter,
solarwinds_orion_adapter,
crowd_strike_adapter,
esx_adapter,
crowd_strike_adapter,
crowd_strike_adapter,
crowd_strike_adapter,
esx_adapter
DC4 192.168.20.17,
192.168.20.58,
fe80::2dba:9118:1fc8:7759,
192.168.20.36,
192.168.20.50,
192.168.20.61
00:0C:29:B6:DA:46,
00:50:56:91:DE:BB,
00:50:56:91:3A:EC,
00:50:56:91:33:E2,
00:50:56:91:21:B3
x.x.x.x/24 d530db3cfef6a2220b315d54fa1901b2

axonius-add-note


Add note to assets

Base Command

axonius-add-note

Input

Argument Name Description Required
note Note to add. Required
ids IDs of assets. Required
type Type of Asset. Device or User. Possible values are: devices, users. Required

Context Output

Path Type Description
Axonius.assets.updates Number Number of assets updated.

axonius-add-tag


Add tag to assets

Base Command

axonius-add-tag

Input

Argument Name Description Required
tag_name Name of tag to add. Required
ids IDs of assets. Required
type Type of Asset. Device or User. Possible values are: devices, users. Required

Context Output

Path Type Description
Axonius.assets.updates Number Number of assets updated.

axonius-remove-tag


Remove tag from assets

Base Command

axonius-remove-tag

Input

Argument Name Description Required
tag_name Name of tag to remove. Required
ids IDs of assets. Required
type Type of Asset. Devices or Users. Possible values are: devices, users. Optional

Context Output

Path Type Description
Axonius.asset.updates Number Number of assets updated.

axonius-get-saved-queries


Get all saved query of a given asset type.

Base Command

axonius-get-saved-queries

Input

Argument Name Description Required
type Type of Asset. Device or User. Possible values are: devices, users. Required

Context Output

Path Type Description
Axonius.Devices.saved_queries Unknown Saved queries.
Axonius.Users.saved_queries Unknown Saved queries.

axonius-get-tags


Get all tags of a given asset type.

Base Command

axonius-get-tags

Input

Argument Name Description Required
type Type of Asset. Device or User. Possible values are: devices, users. Required

Context Output

Path Type Description
Axonius.tags Unknown Axonius Tags.

axonius-get-assets


Fetch assets of any type using the Axonius v2 API (POST /api/v2/assets/{asset_type}). Supports all asset types including vulnerability_instances. Use next_token for pagination. Large responses (>10 MB) may be stored as a downloadable file by XSOAR instead of being written to the context. NOTE: All calls write to the fixed context key Axonius.Assets regardless of asset_type. If a playbook calls this command more than once with different asset types, each call overwrites the previous result. To avoid data loss, store or transform the context output (e.g. using Set) before issuing a second call with a different type.

Base Command

axonius-get-assets

Input

Argument Name Description Required
asset_type The asset type to fetch (e.g. devices, users, vulnerability_instances). See axonius-get-asset-types for all supported values. Required
query The AQL filter string to narrow the returned assets. Optional
fields A comma-separated list of fields to include in the response. Optional
fields_to_exclude A comma-separated list of fields to exclude from the response. Optional
page_size The number of assets to request per page (1–2000). Keep this value small for large asset types (e.g. vulnerability_instances) to avoid XSOAR context size limits (~10 MB). Default is 50. Optional
limit The maximum number of assets to return from the page (defaults to page_size). Optional
next_token The pagination cursor returned from a previous call as Axonius.Assets.next_token. Pass this value to retrieve the next page of results. Optional
include_metadata Whether to include metadata in the response. Possible values are: true, false. Default is false. Optional
include_details Whether to include detailed asset data in the response. Possible values are: true, false. Default is false. Optional
use_cache_entry Whether to use a cached response entry if available. Possible values are: true, false. Default is false. Optional

Context Output

Path Type Description
Axonius.Assets.asset_type String The asset type that was queried.
Axonius.Assets.assets Unknown The list of asset records returned by the query.
Axonius.Assets.count Number The number of assets returned in this page.
Axonius.Assets.total_count Number The total number of assets matching the query (when available).
Axonius.Assets.next_token String The pagination cursor for the next page of results. Pass this as next_token in the next call.

axonius-get-asset-types


Return the list of all available asset types in the Axonius instance (GET /api/v2/assets/asset_types).

Base Command

axonius-get-asset-types

Input

There are no input arguments for this command.

Context Output

Path Type Description
Axonius.AssetTypes.asset_type String The asset type name.
Axonius.AssetTypes Unknown The list of available asset types.

axonius-get-custom-data


List custom data management entries (GET /api/v2/custom_data_management).

Base Command

axonius-get-custom-data

Input

Argument Name Description Required
page The page number to retrieve (1-indexed). Default is 1. Optional
page_size The number of entries to return per page. Default is 50. Optional
limit The maximum number of entries to return (defaults to page_size). Optional

Context Output

Path Type Description
Axonius.CustomData.id String The custom data entry unique identifier.
Axonius.CustomData Unknown The list of custom data entries.

axonius-create-custom-data


Create a new custom data entry (POST /api/v2/custom_data_management).

Base Command

axonius-create-custom-data

Input

Argument Name Description Required
payload The JSON string representing the custom data payload to create. Refer to Axonius API documentation for the required schema. Required

Context Output

Path Type Description
Axonius.CustomData Unknown The created custom data entry.

axonius-delete-custom-data


Delete a custom data entry by ID (DELETE /api/v2/custom_data_management/{id}).

Base Command

axonius-delete-custom-data

Input

Argument Name Description Required
id The ID of the custom data entry to delete. Required

Context Output

Path Type Description
Axonius.CustomData.id String The ID of the deleted custom data entry.
Axonius.CustomData.deleted Boolean The flag indicating whether the entry was deleted.

axonius-get-enforcements


List enforcement sets (GET /api/v2/enforcements).

Base Command

axonius-get-enforcements

Input

Argument Name Description Required
page The page number to retrieve (1-indexed). Default is 1. Optional
page_size The number of enforcements to return per page. Default is 50. Optional
limit The maximum number of enforcements to return (defaults to page_size). Optional

Context Output

Path Type Description
Axonius.Enforcements.uuid String The enforcement unique identifier.
Axonius.Enforcements.name String The enforcement name.
Axonius.Enforcements Unknown The full list of enforcement objects.

axonius-run-enforcement


Trigger an enforcement run (POST /api/v2/enforcements/{enforcement_id}/run).

Base Command

axonius-run-enforcement

Input

Argument Name Description Required
enforcement_id The UUID of the enforcement to run. Required

Context Output

Path Type Description
Axonius.Enforcements.enforcement_id String The ID of the enforcement that was triggered.
Axonius.Enforcements.triggered Boolean The flag indicating whether the enforcement was triggered successfully.

axonius-get-queries


List saved queries (GET /api/v2/queries).

Base Command

axonius-get-queries

Input

Argument Name Description Required
asset_type The asset type to filter queries by (e.g. devices, users). Optional
page The page number to retrieve (1-indexed). Default is 1. Optional
page_size The number of queries to return per page. Default is 50. Optional
limit The maximum number of queries to return (defaults to page_size). Optional

Context Output

Path Type Description
Axonius.Queries.uuid String The query unique identifier.
Axonius.Queries.name String The query name.
Axonius.Queries Unknown The full list of query objects.

axonius-create-query


Create a new saved query (POST /api/v2/queries).

Base Command

axonius-create-query

Input

Argument Name Description Required
name The name for the new saved query. Required
query The AQL filter string for the query. Required
asset_type The asset type this query applies to. Default is devices. Optional
description The optional description for the query. Optional

Context Output

Path Type Description
Axonius.Queries.uuid String The UUID of the created query.
Axonius.Queries.name String The name of the created query.
Axonius.Queries Unknown The created query object.

axonius-delete-query


Delete a saved query (DELETE /api/v2/queries/{query_id}).

Base Command

axonius-delete-query

Input

Argument Name Description Required
query_id The UUID of the query to delete. Required

Context Output

Path Type Description
Axonius.Queries.query_id String The ID of the deleted query.
Axonius.Queries.deleted Boolean The flag indicating whether the query was deleted.

axonius-get-grouped-vulnerabilities


Fetch all vulnerability instances, flatten them, group by CVE ID, and return the Top N CVEs sorted by affected host count. Computes average_cvss_score per CVE. Outputs as Axonius.GroupedVulnerabilities keyed by cve_id.

Base Command

axonius-get-grouped-vulnerabilities

Input

Argument Name Description Required
query The optional AQL filter applied before grouping. Optional
team_name The team name to filter vulnerability instances by. Optional
urgent Whether to filter by urgency on vulnerability instances. Possible values are: true, false. Optional
top_n The number of top CVEs to return (sorted by affected_hosts_count descending). Default is 10. Optional
page_size The number of records per page when fetching all instances. Default is 100. Optional

Context Output

Path Type Description
Axonius.GroupedVulnerabilities.cve_id String The CVE identifier.
Axonius.GroupedVulnerabilities.affected_hosts_count Number The number of hosts affected by this CVE.
Axonius.GroupedVulnerabilities.average_cvss_score Number The average CVSS score across all instances of this CVE.

Configuration parameters

  • ax_url — Server URL (e.g. https://example.net) (required)
  • credentials — API Key (required)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (30)

  • axonius-add-note

    Add note to assets.

  • axonius-add-tag

    Add tag to assets.

  • axonius-create-custom-data

    Create a new custom data entry (POST /api/v2/custom_data_management).

  • axonius-create-query

    Create a new saved query (POST /api/v2/queries).

  • axonius-delete-custom-data

    Delete a custom data entry by ID (DELETE /api/v2/custom_data_management/{id}).

  • axonius-delete-query

    Delete a saved query (DELETE /api/v2/queries/{query_id}).

  • axonius-get-asset-types

    Return the list of all available asset types in the Axonius instance (GET /api/v2/assets/asset_types).

  • axonius-get-assets

    Fetch assets of any type using the Axonius v2 API (POST /api/v2/assets/{asset_type}). Supports all asset types including vulnerability_instances. Use next_token for pagination. Large responses (>10 MB) may be stored as a downloadable file by XSOAR instead of being written to the context. NOTE: All calls write to the fixed context key Axonius.Assets regardless of asset_type. If a playbook calls this command more than once with different asset types, each call overwrites the previous result. To avoid data loss, store or transform the context output (e.g. using Set) before issuing a second call with a different type.

  • axonius-get-custom-data

    List custom data management entries (GET /api/v2/custom_data_management).

  • axonius-get-devices-by-aql

    Gather devices info by AQL query.

  • axonius-get-devices-by-hostname

    Gather device info by hostname.

  • axonius-get-devices-by-hostname-regex

    Gather device info by hostname using regex.

  • axonius-get-devices-by-ip

    Gather device info by IP address.

  • axonius-get-devices-by-ip-regex

    Gather device info by IP address using regex.

  • axonius-get-devices-by-mac

    Gather device info by MAC address.

  • axonius-get-devices-by-mac-regex

    Gather device info by MAC address using regex.

  • axonius-get-devices-by-savedquery

    Gather device info by saved query.

  • axonius-get-enforcements

    List enforcement sets (GET /api/v2/enforcements).

  • axonius-get-grouped-vulnerabilities

    Fetch all vulnerability instances, flatten them, group by CVE ID, and return the Top N CVEs sorted by affected host count. Computes average_cvss_score per CVE. Outputs as Axonius.GroupedVulnerabilities keyed by cve_id.

  • axonius-get-queries

    List saved queries (GET /api/v2/queries).

  • axonius-get-saved-queries

    Get all saved query of a given asset type.

  • axonius-get-tags

    Get all tags of a given asset type.

  • axonius-get-users-by-aql

    Gather users info by AQL query.

  • axonius-get-users-by-mail

    Gather user info by email address.

  • axonius-get-users-by-mail-regex

    Gather user info by email address using regex.

  • axonius-get-users-by-savedquery

    Gather user info by saved query.

  • axonius-get-users-by-username

    Gather user info by username.

  • axonius-get-users-by-username-regex

    Gather user info by username using regex.

  • axonius-remove-tag

    Remove tag from assets.

  • axonius-run-enforcement

    Trigger an enforcement run (POST /api/v2/enforcements/{enforcement_id}/run).

"""Axonius Integration for Cortex XSOAR - Unit Tests file."""

import json
import warnings
from unittest.mock import MagicMock, patch

import pytest

from Axonius import (
    _fetch_all_pages,
    _flatten_instance,
    _handle_api_response,
    get_assets,
    get_asset_types,
    get_custom_data,
    create_custom_data,
    delete_custom_data,
    get_enforcements,
    run_enforcement,
    get_queries,
    create_query,
    delete_query,
    get_grouped_vulnerabilities,
    run_command,
)
from CommonServerPython import DemistoException
from marshmallow.warnings import RemovedInMarshmallow4Warning

from TestData.Expected_data import EXPECTED_DEVICE, EXPECTED_DEVICE_TAGS, EXPECTED_USERS_SQS
from TestData.Raw_data import (
    DUMMY_ASSET_TYPES,
    DUMMY_CUSTOM_DATA,
    DUMMY_DEVICES,
    DUMMY_DEVICES_IDS,
    DUMMY_ENFORCEMENTS,
    DUMMY_QUERIES,
    DUMMY_TAGS,
    DUMMY_USER_IDS,
    DUMMY_V2_ASSETS_RESPONSE,
    DUMMY_V2_ASSETS_RESPONSE_WITH_CURSOR,
    DUMMY_VULNERABILITY_INSTANCES,
    USERS_SQS,
)

warnings.filterwarnings("ignore", category=RemovedInMarshmallow4Warning)


class DummyDevices:
    def __init__(self):
        self.saved_query = DummyDevicesSavedQueries()
        self.labels = DummyDevicesLabels()
        self.LAST_GET = {}

    @staticmethod
    def get_by_hostname(value: str, max_rows: int, fields: list):
        return DUMMY_DEVICES

    @staticmethod
    def get(query: str, max_rows: int, fields: list):
        return DUMMY_DEVICES


class DummyDevicesSavedQueries:
    @staticmethod
    def get():
        return USERS_SQS


class DummyDevicesLabels:
    @staticmethod
    def get():
        return DUMMY_TAGS

    @staticmethod
    def add(rows: list, labels: list):
        return len(DUMMY_DEVICES_IDS)


class DummyUsers:
    def __init__(self):
        self.saved_query = DummyUsersSavedQueries()
        self.labels = DummyUsersLabels()
        self.LAST_GET = {}


class DummyUsersSavedQueries:
    @staticmethod
    def get():
        return USERS_SQS


class DummyUsersLabels:
    @staticmethod
    def get():
        return DUMMY_TAGS

    @staticmethod
    def remove(rows: list, labels: list):
        return len(DUMMY_USER_IDS)


class DummyConnect:
    def __init__(self):
        self.devices = DummyDevices()
        self.users = DummyUsers()

    @staticmethod
    def start():
        return True


def test_client():
    """Pass."""

    client = DummyConnect()
    expected = "ok"
    args: dict = {}
    result = run_command(client=client, args=args, command="test-module")
    assert expected == result


def test_get_saved_queries():
    client = DummyConnect()
    args: dict = {"type": "users"}
    result = run_command(client=client, args=args, command="axonius-get-saved-queries")
    assert len(EXPECTED_USERS_SQS) == len(result.outputs)


def test_get_tags():
    client = DummyConnect()
    args: dict = {"type": "devices"}
    result = run_command(client=client, args=args, command="axonius-get-tags")
    assert result.outputs == EXPECTED_DEVICE_TAGS


def test_add_tags():
    client = DummyConnect()
    args: dict = {"type": "devices", "ids": DUMMY_DEVICES_IDS, "tag_name": "test"}
    result = run_command(client=client, args=args, command="axonius-add-tag")
    assert len(DUMMY_DEVICES_IDS) == result.outputs


def test_remove_tags():
    client = DummyConnect()
    args: dict = {"type": "users", "ids": DUMMY_USER_IDS, "tag_name": "test"}
    result = run_command(client=client, args=args, command="axonius-remove-tag")
    assert len(DUMMY_USER_IDS) == result.outputs


def test_get_device():
    client = DummyConnect()
    args: dict = {"value": "DESKTOP-Gary-Gaither"}
    result = run_command(client=client, args=args, command="axonius-get-devices-by-hostname")
    assert EXPECTED_DEVICE["internal_axon_id"] == result.outputs["internal_axon_id"]


def test_get_by_aql():
    client = DummyConnect()
    args: dict = {"query": '("specific_data.data.name" == regex("john", "i"))'}
    result = run_command(client=client, args=args, command="axonius-get-devices-by-aql")
    assert EXPECTED_DEVICE["internal_axon_id"] == result.outputs["internal_axon_id"]


def test_add_note():
    client = DummyConnect()
    args: dict = {"type": "devices", "ids": DUMMY_DEVICES_IDS, "note": "note1"}
    result = run_command(client=client, args=args, command="axonius-add-note")
    assert result.outputs == 0


# ---------------------------------------------------------------------------
# _handle_api_response tests
# ---------------------------------------------------------------------------


def test_handle_api_response_none_raises():
    with pytest.raises(DemistoException, match="No URL configured"):
        _handle_api_response(None, "api/v2/test")


def test_handle_api_response_non_ok_raises():
    mock_resp = MagicMock()
    mock_resp.ok = False
    mock_resp.status_code = 500
    mock_resp.text = "Internal Server Error"
    with pytest.raises(DemistoException, match="HTTP 500"):
        _handle_api_response(mock_resp, "api/v2/test")


def test_handle_api_response_empty_content_returns_empty_dict():
    mock_resp = MagicMock()
    mock_resp.ok = True
    mock_resp.content = b""
    result = _handle_api_response(mock_resp, "api/v2/test")
    assert result == {}


def test_handle_api_response_valid_json():
    mock_resp = MagicMock()
    mock_resp.ok = True
    mock_resp.content = b'{"data": [1, 2, 3]}'
    mock_resp.json.return_value = {"data": [1, 2, 3]}
    result = _handle_api_response(mock_resp, "api/v2/test")
    assert result == {"data": [1, 2, 3]}


# ---------------------------------------------------------------------------
# axonius-get-assets tests
# ---------------------------------------------------------------------------


def _make_ok_response(body: dict) -> MagicMock:
    mock_resp = MagicMock()
    mock_resp.ok = True
    mock_resp.status_code = 200
    mock_resp.content = json.dumps(body).encode()
    mock_resp.json.return_value = body
    return mock_resp


def test_get_assets_basic():
    with patch("Axonius.make_api_call", return_value=_make_ok_response(DUMMY_V2_ASSETS_RESPONSE)):
        result = get_assets({"asset_type": "vulnerability_instances", "page_size": "2"})
    assert result.outputs["asset_type"] == "vulnerability_instances"
    assert len(result.outputs["assets"]) == 2
    assert result.outputs["count"] == 2
    assert "next_token" not in result.outputs


def test_get_assets_with_cursor():
    with patch("Axonius.make_api_call", return_value=_make_ok_response(DUMMY_V2_ASSETS_RESPONSE_WITH_CURSOR)):
        result = get_assets({"asset_type": "devices"})
    assert result.outputs["next_token"] == "cursor_token_xyz"


def test_get_assets_no_results():
    empty_resp = {"assets": [], "meta": {"page": {}, "next_page": None}}
    with patch("Axonius.make_api_call", return_value=_make_ok_response(empty_resp)):
        result = get_assets({"asset_type": "security_findings"})
    assert result.outputs["assets"] == []
    assert "No" in result.readable_output


def test_get_assets_api_error_raises():
    mock_resp = MagicMock()
    mock_resp.ok = False
    mock_resp.status_code = 403
    mock_resp.text = "Forbidden"
    with patch("Axonius.make_api_call", return_value=mock_resp), pytest.raises(DemistoException, match="HTTP 403"):
        get_assets({"asset_type": "devices"})


# ---------------------------------------------------------------------------
# axonius-get-asset-types tests
# ---------------------------------------------------------------------------


def test_get_asset_types():
    body = {"asset_types": DUMMY_ASSET_TYPES}
    with patch("Axonius.make_api_call", return_value=_make_ok_response(body)):
        result = get_asset_types()
    assert any(entry["asset_type"] == "devices" for entry in result.outputs)


# ---------------------------------------------------------------------------
# Custom Data Management tests
# ---------------------------------------------------------------------------


def test_get_custom_data():
    body = {"custom_fields": DUMMY_CUSTOM_DATA}
    with patch("Axonius.make_api_call", return_value=_make_ok_response(body)):
        result = get_custom_data({})
    assert len(result.outputs) == 2


def test_create_custom_data_valid():
    payload = json.dumps({"name": "new_field", "value": "new_value"})
    body = {"id": "cd-003", "name": "new_field", "value": "new_value"}
    with patch("Axonius.make_api_call", return_value=_make_ok_response(body)):
        result = create_custom_data({"payload": payload})
    assert "created" in result.readable_output


def test_create_custom_data_no_payload_raises():
    with pytest.raises(DemistoException, match="'payload' argument is required"):
        create_custom_data({})


def test_create_custom_data_invalid_json_raises():
    with pytest.raises(DemistoException, match="Invalid JSON"):
        create_custom_data({"payload": "not-json"})


def test_delete_custom_data():
    mock_resp = MagicMock()
    mock_resp.ok = True
    mock_resp.content = b""
    with patch("Axonius.make_api_call", return_value=mock_resp):
        result = delete_custom_data({"id": "cd-001"})
    assert result.outputs["deleted"] is True
    assert result.outputs["id"] == "cd-001"


def test_delete_custom_data_no_id_raises():
    with pytest.raises(DemistoException, match="'id' argument is required"):
        delete_custom_data({})


# ---------------------------------------------------------------------------
# Enforcements tests
# ---------------------------------------------------------------------------


def test_get_enforcements():
    body = {"enforcements": DUMMY_ENFORCEMENTS}
    with patch("Axonius.make_api_call", return_value=_make_ok_response(body)):
        result = get_enforcements({})
    assert len(result.outputs) == 2


def test_run_enforcement():
    body = {"status": "triggered"}
    with patch("Axonius.make_api_call", return_value=_make_ok_response(body)):
        result = run_enforcement({"enforcement_id": "enf-001"})
    assert result.outputs["triggered"] is True
    assert result.outputs["enforcement_id"] == "enf-001"


def test_run_enforcement_no_id_raises():
    with pytest.raises(DemistoException, match="'enforcement_id' argument is required"):
        run_enforcement({})


# ---------------------------------------------------------------------------
# Queries tests
# ---------------------------------------------------------------------------


def test_get_queries():
    body = {"queries": DUMMY_QUERIES}
    with patch("Axonius.make_api_call", return_value=_make_ok_response(body)):
        result = get_queries({})
    assert len(result.outputs) == 2


def test_create_query():
    body = {"uuid": "q-new", "name": "My Query", "asset_type": "devices"}
    with patch("Axonius.make_api_call", return_value=_make_ok_response(body)):
        result = create_query({"name": "My Query", "query": '("specific_data.data.name" == "test")', "asset_type": "devices"})
    assert "created" in result.readable_output


def test_create_query_missing_name_raises():
    with pytest.raises(DemistoException, match="'name' argument is required"):
        create_query({"query": "some query"})


def test_create_query_missing_query_raises():
    with pytest.raises(DemistoException, match="'query' argument is required"):
        create_query({"name": "my query"})


def test_delete_query():
    mock_resp = MagicMock()
    mock_resp.ok = True
    mock_resp.content = b""
    with patch("Axonius.make_api_call", return_value=mock_resp):
        result = delete_query({"query_id": "q-001"})
    assert result.outputs["deleted"] is True


def test_delete_query_no_id_raises():
    with pytest.raises(DemistoException, match="'query_id' argument is required"):
        delete_query({})


# ---------------------------------------------------------------------------
# _flatten_instance tests
# ---------------------------------------------------------------------------


def test_flatten_instance_single_element_list():
    inst = {"hostname": ["my-host"], "cvss_score": 9.8}
    flat = _flatten_instance(inst)
    assert flat["hostname"] == "my-host"
    assert flat["cvss_score"] == 9.8


def test_flatten_instance_multi_element_list_unchanged():
    inst = {"ips": ["1.1.1.1", "2.2.2.2"]}
    flat = _flatten_instance(inst)
    assert flat["ips"] == ["1.1.1.1", "2.2.2.2"]


# ---------------------------------------------------------------------------
# axonius-get-grouped-vulnerabilities tests
# ---------------------------------------------------------------------------


def _make_vi_page(assets: list, cursor: str = None) -> dict:
    return {
        "assets": assets,
        "meta": {"page": {"totalResources": len(assets)}, "next_page": cursor},
    }


def test_get_grouped_vulnerabilities_groups_correctly():
    page = _make_vi_page(DUMMY_VULNERABILITY_INSTANCES)
    with patch("Axonius.make_api_call", return_value=_make_ok_response(page)):
        result = get_grouped_vulnerabilities({"top_n": "10"})

    outputs = result.outputs
    cve_map = {r["cve_id"]: r for r in outputs}

    # CVE-2023-1111 has 3 instances, CVE-2023-2222 has 2
    assert cve_map["CVE-2023-1111"]["affected_hosts_count"] == 3
    assert cve_map["CVE-2023-2222"]["affected_hosts_count"] == 2

    # Sorted descending by affected_hosts_count
    assert outputs[0]["cve_id"] == "CVE-2023-1111"


def test_get_grouped_vulnerabilities_average_cvss():
    page = _make_vi_page(DUMMY_VULNERABILITY_INSTANCES)
    with patch("Axonius.make_api_call", return_value=_make_ok_response(page)):
        result = get_grouped_vulnerabilities({"top_n": "10"})

    cve_map = {r["cve_id"]: r for r in result.outputs}
    # CVE-2023-1111: scores [9.8, 9.8, 9.5] → avg = 9.7
    assert abs(cve_map["CVE-2023-1111"]["average_cvss_score"] - 9.7) < 0.01


def test_get_grouped_vulnerabilities_top_n():
    """Verify top_n slicing — pass top_n=1 directly in args."""
    page = _make_vi_page(DUMMY_VULNERABILITY_INSTANCES)
    with patch("Axonius.make_api_call", return_value=_make_ok_response(page)):
        result = get_grouped_vulnerabilities({"top_n": "1"})
    assert len(result.outputs) == 1


def test_get_grouped_vulnerabilities_no_instances():
    page = _make_vi_page([])
    with patch("Axonius.make_api_call", return_value=_make_ok_response(page)):
        result = get_grouped_vulnerabilities({})
    assert result.outputs == []
    assert "No vulnerability" in result.readable_output


def test_fetch_all_pages_pagination():
    """Verify _fetch_all_pages follows cursors until exhausted."""
    page1 = _make_vi_page(DUMMY_VULNERABILITY_INSTANCES[:2], cursor="page2")
    page2 = _make_vi_page(DUMMY_VULNERABILITY_INSTANCES[2:], cursor=None)

    with patch(
        "Axonius.make_api_call",
        side_effect=[
            _make_ok_response(page1),
            _make_ok_response(page2),
        ],
    ):
        all_assets = _fetch_all_pages("vulnerability_instances")

    assert len(all_assets) == len(DUMMY_VULNERABILITY_INSTANCES)