Skip to content

Commit

Permalink
Contrib/binalyze air update (#30161) (#30950)
Browse files Browse the repository at this point in the history
* Acquisition profiles extended to manual created ones.
Organization_id vale type bug fixed.
Minor changes in README.md file.

* Acquisition profiles extended to manual created ones.
Organization_id vale type bug fixed.
Minor changes in README.md file.

* Acquisition profiles extended to manual created ones.
Organization_id vale type bug fixed.
Minor changes in README.md file.

* Docker version updated

* Unrelated line deleted.

* Acquisition profile changed.
arg_to_number is used for type change.

* organization id type check changed from str to any in both commands.

* Get profile_id function created under Client class.
Data type check moved under class's functions.

* Test profile id added.

* profile id added to the command test coverage.

* New_line addressed: https://app.circleci.com/pipelines/github/demisto/content/309467/workflows/b6a8cb09-0e6b-4ea7-a1e9-2650ac98a978/jobs/724183?invite=true#step-120-7123_48

* Conflict resolved

* Conflict resolved

* Suggestion accepted.

* Versioning fixed.

* - Get profile id changed: preset values does not have id, so it needs to pbe passed as is.
- Arg_to_number moved to the commands section as advised.

* - Resolves https://app.circleci.com/pipelines/github/demisto/content/310327/workflows/c343f258-6e56-4098-a697-345d30ba452a/jobs/724633/parallel-runs/0/steps/0-119

* - str to any.

* Rolled back the changes because of the comments, test suite(mypy) yells at me.

* Type change is alligned with reviewer.

* Predefined settings have changed.

* revert RN change

* This resolves #30161 (comment)

* LAst comments fixed.

* Reverted to predefined.
Added more Org is

* Custom profile command example added.

* Documentation updated.

* Get profile id test case added.

* docker image update

* docker image update

* Context path junk string purged.

* Update Packs/Binalyze/Integrations/BinalyzeAIR/BinalyzeAIR_test.py



* Update Packs/Binalyze/Integrations/BinalyzeAIR/BinalyzeAIR_test.py



* Whitespaces cleaned.

* Last review's issues addressed.

* add predfined description

* Minor changes on descriptions.

* Demo review implemented.

* Demo review implemented.

* Error handling removed.

* Test case fixed.

* Test case minor change.

* Test case minor change.

* Test case minor change.

* Newline and typo

* Update BinalyzeAIR_test.py

---------

Co-authored-by: binalyze-murat <106888581+binalyze-murat@users.noreply.github.com>
Co-authored-by: sapirshuker <sshuker@paloaltonetworks.com>
Co-authored-by: Sapir Shuker <49246861+sapirshuker@users.noreply.github.com>
  • Loading branch information
4 people committed Nov 16, 2023
1 parent 9ec4820 commit 01e9451
Show file tree
Hide file tree
Showing 10 changed files with 205 additions and 115 deletions.
50 changes: 41 additions & 9 deletions Packs/Binalyze/Integrations/BinalyzeAIR/BinalyzeAIR.py
@@ -1,6 +1,6 @@
import demistomock as demisto # noqa: F401
from CommonServerPython import * # noqa: F401
from typing import Dict, Any
from typing import Dict, Any, Optional

import urllib3

Expand All @@ -15,11 +15,43 @@ def test_api(self):
url_suffix='/api/public/endpoints?filter[organizationIds]=0'
)

def air_acquire(self, hostname: str, profile: str, case_id: str, organization_id: int) -> Dict[str, str]:
def get_profile_id(self, profile: str, organization_id: Optional[int]) -> str:
'''Gets the profile ID based on the profile name and organization ID by making a GET request to the
'/api/public/acquisitions/profiles' endpoint.
Args:
profile (str): The name of the profile to query.
organization_id (int): The organization ID associated with the profile.
Returns:
str: The profile ID obtained from the API response.
Raises:
DemistoException: If there is an error making the HTTP request or processing the API response.
'''
preset_profiles = ["browsing-history", "compromise-assessment", "event-logs", "full", "memory-ram-pagefile", "quick"]
if profile in preset_profiles:
return profile
else:
result = self._http_request(
method='GET',
url_suffix=f'/api/public/acquisitions/profiles?filter[name]={profile}&filter[organizationIds]='
f'{organization_id}').get("result", {}).get("entities", [])
profile_id = ""
for entity in result:
if entity.get("name") == profile:
profile_id = entity.get("_id")
if profile_id:
return profile_id
# There is no match with profile_id.
if not profile_id:
return_error(f'The acquisition profile "{profile}" cannot be found. Please ensure that you enter a valid '
f'profile name.')
return ""

def air_acquire(self, hostname: str, profile: str, case_id: str, organization_id: Optional[int]) -> Dict[str, Any]:
''' Makes a POST request /api/public/acquisitions/acquire endpoint to verify acquire evidence
:param hostname str: endpoint hostname to start acquisition.
:param profile str: predefined 5 acquisiton profile name.
:param profile str: get the profile string makes a query, and uses profile_id for mapping correct profile.
:param case_id str: The Case ID to associate with in AIR Server.
:param organization_id int: Organizsation ID of the endpoint.
Expand All @@ -37,7 +69,7 @@ def air_acquire(self, hostname: str, profile: str, case_id: str, organization_id
"taskConfig": {
"choice": "use-policy"
},
"acquisitionProfileId": profile,
"acquisitionProfileId": self.get_profile_id(profile, organization_id),
"filter": {
"name": hostname,
"organizationIds": [organization_id]
Expand All @@ -49,7 +81,7 @@ def air_acquire(self, hostname: str, profile: str, case_id: str, organization_id
json_data=payload
)

def air_isolate(self, hostname: str, organization_id: int, isolation: str) -> Dict[str, str]:
def air_isolate(self, hostname: str, organization_id: Optional[int], isolation: str) -> Dict[str, Any]:
''' Makes a POST request /api/public/acquisitions/acquire endpoint to verify acquire evidence
:param hostname str: endpoint hostname to start acquisition.
:param isolation str: To isolate enable, to disable isolate use disable
Expand All @@ -60,7 +92,7 @@ def air_isolate(self, hostname: str, organization_id: int, isolation: str) -> Di
:rtype Dict[str, Any]
'''

payload: Dict[str, Any] = {
payload: Dict[Any, Any] = {
"enabled": True,
"filter": {
"name": hostname,
Expand Down Expand Up @@ -100,8 +132,8 @@ def air_acquire_command(client: Client, args: Dict[str, Any]) -> CommandResults:
case_id = args.get('case_id', '')
organization_id = args.get('organization_id', '')

result: Dict[str, Any] = client.air_acquire(hostname, profile, case_id, organization_id)
readable_output = tableToMarkdown('Binalyze AIR Isolate Results', result,
result: Dict[str, Any] = client.air_acquire(hostname, profile, case_id, arg_to_number(organization_id))
readable_output = tableToMarkdown('Binalyze AIR Acquisition Results', result,
headers=('success', 'result', 'statusCode', 'errors'),
headerTransform=string_to_table_header)

Expand All @@ -126,7 +158,7 @@ def air_isolate_command(client: Client, args: Dict[str, Any]) -> CommandResults:
organization_id = args.get('organization_id', '')
isolation = args.get('isolation', '')

result: Dict[str, Any] = client.air_isolate(hostname, organization_id, isolation)
result: Dict[Any, Any] = client.air_isolate(hostname, arg_to_number(organization_id), isolation)
readable_output = tableToMarkdown('Binalyze AIR Isolate Results', result,
headers=('success', 'result', 'statusCode', 'errors'),
headerTransform=string_to_table_header)
Expand Down
8 changes: 4 additions & 4 deletions Packs/Binalyze/Integrations/BinalyzeAIR/BinalyzeAIR.yml
Expand Up @@ -40,7 +40,7 @@ script:
- '0'
- '1'
- '2'
description: Organization ID of the endpoint.
description: Organization ID of the endpoint. For a custom organization ID, you can specify a custom value outside the predefined set.
- name: isolation
required: true
auto: PREDEFINED
Expand Down Expand Up @@ -74,7 +74,7 @@ script:
- memory-ram-pagefile
- quick
- full
description: Acquisition profile.
description: Acquisition profile. To use a custom acquisition profile, you can specify a custom profile outside the predefined set.
- name: case_id
required: true
description: ID for the case,e.g. C-2022-0001.
Expand All @@ -85,13 +85,13 @@ script:
- '0'
- '1'
- '2'
description: Organization ID of the endpoint.
description: Organization ID of the endpoint. For a custom organization ID, you can specify a custom value outside the predefined set.
outputs:
- contextPath: BinalyzeAIR.Acquire.result._id
description: Acquisition unique task ID.
type: string
- contextPath: BinalyzeAIR.Acquire.result.name
description: Acquisiton task name.
description: Acquisition task name.
type: string
- contextPath: BinalyzeAIR.Acquire.result.organizationId
description: Organization Id of endpoint.
Expand Down
Expand Up @@ -15,7 +15,9 @@ You can use the integration in Automation, Playbooks, or Playground.
**Acquisition**
- !air-acquire *hostname*=\<HOSTNAMEofENDPOINT\> *profile*=\<DEFINED PROFILE\> *caseid*=\<The Case ID\>

**Defined Profiles:**
*You can use custom acquisition profiles you created in the AIR.*

**Pre-defined Profiles:**
- browsing-history
- compromise-assessment
- event-logs
Expand Down
42 changes: 33 additions & 9 deletions Packs/Binalyze/Integrations/BinalyzeAIR/BinalyzeAIR_test.py
Expand Up @@ -4,8 +4,6 @@
from CommonServerPython import *
from typing import Dict, Any

MOCK_URL = 'https://nonexistent-domain.com'


def util_load_json(path):
with io.open(path, mode='r', encoding='utf-8') as f:
Expand Down Expand Up @@ -76,16 +74,40 @@ def test_api_connection_fail(requests_mock: Any) -> None:
assert mocked_command_result == expected_mocked_command_result


def test_get_profile_id_preset() -> None:
from BinalyzeAIR import Client
client: Client = Client(
base_url='https://nonexistent-domain.com',
verify=False
)
mocked_profile = "full"
result = client.get_profile_id(mocked_profile, 1)
assert result == mocked_profile


def test_get_profile_id_custom(requests_mock: Any) -> None:
from BinalyzeAIR import Client
mock_response = util_load_json('test_data/profile_id.json')
requests_mock.get('https://nonexistent-domain.com/api/public/acquisitions/profiles?'
'filter[name]=profile&filter[organizationIds]=0',
json=mock_response)
client: Client = Client(
base_url='https://nonexistent-domain.com',
verify=False
)
result = client.get_profile_id("profile", 0)
expected_mocked_profile_id = mock_response.get('result', {}).get('entities', {})[0].get('_id', None)
assert expected_mocked_profile_id == result


def test_air_acquire_command(requests_mock: Any) -> None:
from BinalyzeAIR import Client, air_acquire_command

args: Dict[str, Any] = {
'hostname': 'endpointhostname',
'profile': 'quick',
'profile': "quick",
'case_id': 'case_id will be here',
'organization_id': 0
}

headers: Dict[str, Any] = {
'Authorization': 'Bearer api_key',
'User-Agent': 'Binalyze AIR',
Expand All @@ -95,11 +117,14 @@ def test_air_acquire_command(requests_mock: Any) -> None:
mock_response = util_load_json('test_data/test_acquire_success.json')

client: Client = Client(
base_url=MOCK_URL,
base_url='https://nonexistent-domain.com',
verify=False,
headers=headers
)

mock_get_response = util_load_json('test_data/profile_id.json')
requests_mock.get('https://nonexistent-domain.com/api/public/acquisitions/profiles?'
'filter[name]=profile_name&filter[organizationIds]=0',
json=mock_get_response)
requests_mock.post('https://nonexistent-domain.com/api/public/acquisitions/acquire', json=mock_response)

mocked_command_result: CommandResults = air_acquire_command(client, args)
Expand All @@ -118,7 +143,6 @@ def test_air_acquire_command(requests_mock: Any) -> None:

def test_air_isolate_command(requests_mock: Any) -> None:
from BinalyzeAIR import Client, air_isolate_command

args: Dict[str, Any] = {
'hostname': 'endpointhostname',
'organization_id': 0,
Expand All @@ -133,7 +157,7 @@ def test_air_isolate_command(requests_mock: Any) -> None:
mock_response = util_load_json('test_data/test_isolate_success.json')

client: Client = Client(
base_url=MOCK_URL,
base_url='https://nonexistent-domain.com',
verify=False,
headers=headers
)
Expand Down
148 changes: 76 additions & 72 deletions Packs/Binalyze/Integrations/BinalyzeAIR/README.md
@@ -1,73 +1,77 @@
## Binalyze AIR Integration
This integration allows you to use the Binalyze AIR's isolation and evidence collecting features easily.
---

Collect your forensics data under 10 minutes.
This integration was integrated and tested with version 2.6.2 of Binalyze AIR

## Configure Binalyze AIR on Cortex XSOAR

1. Navigate to **Settings** > **Integrations** > **Servers & Services**.
2. Search for Binalyze AIR.
3. Click **Add instance** to create and configure a new integration instance.
4. Click **Test** to validate the URLs, token, and connection.

| **Parameter** | **Description** | **Required** |
| --- | --- | --- |
| Binalyze AIR Server URL | Binalyze AIR Server URL | True |
| API Key | e.g.: api_1234567890abcdef1234567890abcdef | True |
| Trust any certificate (not secure) | | False |
| Use system proxy settings | | False |
## Commands
You can execute these commands from the Cortex XSOAR 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.
### binalyze-air-isolate
***
Isolate an endpoint


#### Base Command

`binalyze-air-isolate`
#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| hostname | Hostname of endpoint. | Required |
| organization_id | Organization ID of the endpoint. Possible values are: 0, 1, 2. | Required |
| isolation | To isolate use enable. Possible values are: enable, disable. | Required |


#### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| BinalyzeAIR.Isolate.result._id | string | Isolation unique task ID |
| BinalyzeAIR.Isolate.result.name | string | Isolation task name |
| BinalyzeAIR.Isolate.result.organizationId | number | Organization Id of endpoint |

### binalyze-air-acquire
***
Acquire evidence from an endpoint


#### Base Command

`binalyze-air-acquire`
#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| hostname | Hostname of endpoint. | Required |
| profile | Acquisition profile. Possible values are: compromise-assessment, browsing-history, event-logs, memory-ram-pagefile, quick, full. | Required |
| case_id | ID for the case,e.g. C-2022-0001. | Required |
| organization_id | Organization ID of the endpoint. Possible values are: 0, 1, 2. | Required |


#### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| BinalyzeAIR.Acquire.result._id | string | Acquisition unique task ID |
| BinalyzeAIR.Acquire.result.name | string | Acquisiton task name |
## Binalyze AIR Integration
This integration allows you to use the Binalyze AIR's isolation and evidence collecting features easily.
---

Collect your forensics data under 10 minutes.
This integration was integrated and tested with version 2.6.2 of Binalyze AIR

## Configure Binalyze AIR on Cortex XSOAR

1. Navigate to **Settings** > **Integrations** > **Servers & Services**.
2. Search for Binalyze AIR.
3. Click **Add instance** to create and configure a new integration instance.
4. Click **Test** to validate the URLs, token, and connection.

| **Parameter** | **Description** | **Required** |
| --- | --- | --- |
| Binalyze AIR Server URL | Binalyze AIR Server URL | True |
| API Key | e.g.: api_1234567890abcdef1234567890abcdef | True |
| Trust any certificate (not secure) | | False |
| Use system proxy settings | | False |

## Commands

You can execute these commands from the Cortex XSOAR 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.

### binalyze-air-isolate

***
Isolate an endpoint


#### Base Command

`binalyze-air-isolate`
#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| hostname | Hostname of endpoint. | Required |
| organization_id | Organization ID of the endpoint. For the use of a custom organization ID, you can specify a custom value outside the predefined set. | Required |
| isolation | To isolate use enable. Possible values are: enable, disable. | Required |


#### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| BinalyzeAIR.Isolate.result._id | string | Isolation unique task ID |
| BinalyzeAIR.Isolate.result.name | string | Isolation task name |
| BinalyzeAIR.Isolate.result.organizationId | number | Organization Id of endpoint |

### binalyze-air-acquire
***
Acquire evidence from an endpoint


#### Base Command

`binalyze-air-acquire`
#### Input

| **Argument Name** | **Description** | **Required** |
| --- |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| --- |
| hostname | Hostname of endpoint. | Required |
| profile | Acquisition profile. To use a custom acquisition profile, you can specify a custom value outside the predefined set. Possible values are: compromise-assessment, browsing-history, event-logs, memory-ram-pagefile, quick, full. | Required |
| case_id | ID for the case,e.g. C-2022-0001. | Required |
| organization_id | Organization ID of the endpoint. For the use of a custom organization ID, you can specify a custom value outside the predefined set. | Required |


#### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| BinalyzeAIR.Acquire.result._id | string | Acquisition unique task ID |
| BinalyzeAIR.Acquire.result.name | string | Acquisiton task name |
| BinalyzeAIR.Acquire.result.organizationId | number | Organization Id of endpoint |

0 comments on commit 01e9451

Please sign in to comment.