Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Netskope event collector rewrite #28941

Merged
merged 47 commits into from
Aug 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
0c85fa7
Temporarily added the following packs to the update_core_packs_list:
ShahafBenYakir Feb 19, 2023
63e3e61
Merge branch 'master' of github.com:demisto/content into add_packs_to…
ShahafBenYakir Feb 19, 2023
3ea7a87
Merge branch 'master' of github.com:demisto/content into add_packs_to…
ShahafBenYakir Feb 20, 2023
c81502c
Added all packs to update core list
ShahafBenYakir Feb 20, 2023
dac166b
Merge branch 'master' into add_packs_to_update_core_packs_list
ShahafBenYakir Feb 21, 2023
52ae21e
Merge branch 'master' of github.com:demisto/content into add_packs_to…
ShahafBenYakir Feb 21, 2023
67836fe
Merge branch 'add_packs_to_update_core_packs_list' of github.com:demi…
ShahafBenYakir Feb 21, 2023
2481530
Merge branch 'master' into add_packs_to_update_core_packs_list
ShahafBenYakir Feb 21, 2023
6696d49
merge from master
ShahafBenYakir Mar 8, 2023
ff8111d
Added al core packs to update_core_packs_list
ShahafBenYakir Mar 8, 2023
7ed95de
Added new API endpoint
ShahafBenYakir Jun 14, 2023
8505f2c
Merge branch 'master' of github.com:demisto/content into Netskope_eve…
ShahafBenYakir Jul 4, 2023
7064f29
Added testing copies
ShahafBenYakir Jul 6, 2023
578602a
Merge branch 'master' of github.com:demisto/content into Netskope_eve…
ShahafBenYakir Jul 9, 2023
53a8e55
changes from testing
ShahafBenYakir Jul 9, 2023
ae4c5ff
changes from testing
ShahafBenYakir Jul 10, 2023
be45264
changes from testing
ShahafBenYakir Jul 10, 2023
30face2
Merge branch 'master' into Netskope_event_collector_rewrite
ShahafBenYakir Jul 18, 2023
fd2f041
Changed default first fecth
ShahafBenYakir Jul 18, 2023
3f70fc0
Added slipping for no wait time
ShahafBenYakir Jul 26, 2023
8e78a0e
First code change
ShahafBenYakir Jul 31, 2023
fd97b29
Fixed description and log
ShahafBenYakir Aug 1, 2023
d9f66ee
Merge branch 'master' of github.com:demisto/content into Netskope_eve…
ShahafBenYakir Aug 1, 2023
2490614
Merge branch 'add_packs_to_update_core_packs_list_2' into Netskope_ev…
ShahafBenYakir Aug 9, 2023
3a098a7
Merge branch 'master' into Netskope_event_collector_rewrite
ShahafBenYakir Aug 9, 2023
7ec267d
UT fixes + mypy
ShahafBenYakir Aug 9, 2023
a760740
Merge branch 'master' of github.com:demisto/content into Netskope_eve…
ShahafBenYakir Aug 13, 2023
f633f32
Merge branch 'master' of github.com:demisto/content into Netskope_eve…
ShahafBenYakir Aug 14, 2023
4d1fe2e
UT fixes + mypy
ShahafBenYakir Aug 14, 2023
0ae33f4
Bumped Docker image and added rn
ShahafBenYakir Aug 14, 2023
57b1ff8
Formatting and typos
ShahafBenYakir Aug 14, 2023
8012baa
Fixed honor_rate_limit and added ut
ShahafBenYakir Aug 16, 2023
74360f1
Flake 8 fix
ShahafBenYakir Aug 16, 2023
c21be2a
Merge branch 'master' of github.com:demisto/content into Netskope_eve…
ShahafBenYakir Aug 16, 2023
74838ea
Added more UT
ShahafBenYakir Aug 16, 2023
9a07e4a
Merge branch 'master' of github.com:demisto/content into Netskope_eve…
ShahafBenYakir Aug 16, 2023
d683fa4
revert core list change
ShahafBenYakir Aug 16, 2023
6c071c5
Enhanced docs
ShahafBenYakir Aug 16, 2023
3e3a321
Small UT fixes
ShahafBenYakir Aug 17, 2023
f2c392e
Removed is_command variable
ShahafBenYakir Aug 17, 2023
b3ec808
Merge branch 'master' into Netskope_event_collector_rewrite
ShahafBenYakir Aug 20, 2023
e0d8973
Added docs
ShahafBenYakir Aug 20, 2023
c9a58b2
changes rn version
ShahafBenYakir Aug 20, 2023
f7f2ce3
- Removed first_fetch param
ShahafBenYakir Aug 23, 2023
bde04f4
Merge branch 'master' into Netskope_event_collector_rewrite
ShahafBenYakir Aug 24, 2023
8dc1dd8
lint fixes
ShahafBenYakir Aug 24, 2023
2f8f0a3
lint fixes
ShahafBenYakir Aug 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
category: Analytics & SIEM

Check failure on line 1 in Packs/Netskope/Integrations/NetskopeEventCollector/NetskopeEventCollector.yml

View workflow job for this annotation

GitHub Actions / pre-commit

Validation Error IN124

Parameter: "credentials" can't be hidden in all marketplaces. Please either remove the `hidden` attribute, or replace its value with a list of marketplace names, where you wish it to be hidden.

Check failure on line 1 in Packs/Netskope/Integrations/NetskopeEventCollector/NetskopeEventCollector.yml

View workflow job for this annotation

GitHub Actions / pre-commit

Validation Error IN129

You've removed integration parameters, the removed parameters are '{'api_version', 'first_fetch'}'
sectionOrder:
- Connect
- Collect
Expand All @@ -17,28 +17,12 @@
required: true
type: 9
section: Connect
- additionalinfo: The API version to use (v1 or v2).
defaultvalue: v2
display: API Version
name: api_version
type: 15
section: Connect
options:
- v1
- v2
required: false
- additionalinfo: 'The first fetch is limited to the last 50,000 events (for each event type).'
defaultvalue: 3 days
display: First fetch timestamp (<number> <time unit>, e.g., 12 hours, 7 days)
name: first_fetch
section: Collect
type: 0
required: false
- additionalinfo: The maximum amount of events to retrieve for each event type (up to 30000 events). For more information about event types see the help section.
defaultvalue: '1000'
hidden: true
- additionalinfo: 'The maximum amount of events to retrieve PER EACH EVENT TYPE type. For more information about event types see the help section.'
defaultvalue: '50000'
Shellyber marked this conversation as resolved.
Show resolved Hide resolved
display: Max events per fetch
section: Collect
name: max_fetch
section: Collect
type: 0
required: false
- display: Trust any certificate (not secure)
Expand Down Expand Up @@ -67,9 +51,10 @@
required: true
- description: The maximum number of alerts to return (maximum value - 10000).
name: limit
defaultValue: 10000
description: Returns events extracted from SaaS traffic and or logs.
name: netskope-get-events
dockerimage: demisto/python3:3.10.12.66339
dockerimage: demisto/python3:3.10.12.68714
runonce: false
script: '-'
subtype: python3
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
### Netskope Event Collector
## Netskope Event Collector

### General Info
- Collects events extracted from SaaS traffic and logs.
- The collector collects 5 types of events:
- Audit
- Application
- Network
- Alert
- Page
- To generate the API token, in your Netskope UI go to **Settings** > **Tools** > **Rest API v1 or v2**
- Please make sure to choose the appropriate **API token** according to the chosen **API Version**.
- Visit the [Netskope API Overview](https://docs.netskope.com/en/rest-api-v2-overview-312207.html) for more information.


- Note: The collector can handle 10K events per minute on average per each event type.

### API Key
- To generate the API token, in your Netskope UI go to **Settings** > **Tools** > **Rest API v2**
- The KEY requires the following permissions:
- /api/v2/events/dataexport/events/*
- /api/v2/events/dataexport/alerts/*
- Visit the [Netskope API Overview](https://docs.netskope.com/en/rest-api-v2-overview-312207.html) for more information.
Original file line number Diff line number Diff line change
@@ -1,46 +1,28 @@
import io
import json
import re
import time

from NetskopeEventCollector import Client
import dateparser
import pytest

from NetskopeEventCollector import Client, ALL_SUPPORTED_EVENT_TYPES, RATE_LIMIT_REMAINING, RATE_LIMIT_RESET


def util_load_json(path):
with io.open(path, mode='r', encoding='utf-8') as f:
return json.loads(f.read())


MOCK_ENTRY = util_load_json('test_data/mock_events.json')
EVENTS_RAW_V2 = util_load_json('test_data/events_raw_v2.json')
EVENTS_RAW_V2_MULTI = util_load_json('test_data/events_raw_v2_2_results.json')
EVENTS_PAGE_RAW_V1 = util_load_json('test_data/page_raw_v1.json')
MOCK_ENTRY = util_load_json('test_data/mock_events_entry.json')
EVENTS_RAW = util_load_json('test_data/events_raw.json')
EVENTS_PAGE_RAW = util_load_json('test_data/multiple_events_raw.json')
BASE_URL = 'https://netskope.example.com'
FIRST_LAST_RUN = {'alert': 1680182467, 'alert-ids': [], 'application': 1680182467, 'application-ids': [],
'audit': 1680182467, 'audit-ids': [], 'network': 1680182467, 'network-ids': [],
'page': 1680182467, 'page-ids': []}


def test_dedup_by_id():
"""
Given:
- Results from the API
When:
- Running the dedup_by_id command
Then:
- Make sure only the limited number of events return.
- Make sure that first comes the event that with the earlier timestamp
- Make sure that the last_run timestamp has been updated
- Make sure that the correct last_run_ids returned.
"""
from NetskopeEventCollector import dedup_by_id
results = EVENTS_PAGE_RAW_V1.get('data')
events, new_last_run = dedup_by_id(last_run=FIRST_LAST_RUN, event_type='page', limit=4, results=results)
assert events[0].get('timestamp') == 1684751415
assert len(events) == 4
assert new_last_run == {'page': 1684751416, 'page-ids': ['3757761212778242bfda29cd', '9e99b72b957416a43222fa7a',
'66544bf5fda515f229592644', '98938eb19b4f9bea24ef9a8c']}
FIRST_LAST_RUN = {'alert': {'operation': 1680182467}, 'application': {'operation': 1680182467},
'audit': {'operation': 1680182467}, 'network': {'operation': 1680182467}, 'page': {'operation': 1680182467}}


def test_test_module_v2(mocker):
def test_test_module(mocker):
"""
Given:
- raw_response of an event (as it returns from the api)
Expand All @@ -50,28 +32,30 @@ def test_test_module_v2(mocker):
- Verify that 'ok' is returned.
"""
from NetskopeEventCollector import test_module
client = Client(BASE_URL, 'dummy_token', 'v2', False, False)
mocker.patch.object(client, 'get_events_request_v2', return_value=EVENTS_RAW_V2)
results = test_module(client, api_version='v2', last_run=FIRST_LAST_RUN, max_fetch=1)
client = Client(BASE_URL, 'dummy_token', False, False)
mocker.patch.object(client, 'perform_data_export', return_value=EVENTS_RAW)
results = test_module(client, last_run=FIRST_LAST_RUN, max_fetch=1)
assert results == 'ok'


def test_populate_parsing_rule_fields():
def test_populate_prepare_events():
"""
Given:
- Event from the API of type audit
When:
- Running the command
Then:
- Make sure the field _time is populated properly.
- Make sure the _time, evnet_id, and source_log_event fields are populated properly.
"""
from NetskopeEventCollector import populate_parsing_rule_fields
event = EVENTS_RAW_V2.get('result')[0]
populate_parsing_rule_fields(event, event_type='audit')
from NetskopeEventCollector import prepare_events
event = EVENTS_RAW.get('result')[0]
prepare_events([event], event_type='audit')
assert event.get('_time') == '2022-01-18T19:58:07.000Z'
assert event.get('source_log_event') == 'audit'
assert event.get('event_id') == 'f0e9b2cadd17402b59b3938b'


def test_get_all_events(mocker):
def test_get_all_events(requests_mock):
"""
Given:
- netskope-get-events call
Expand All @@ -82,18 +66,20 @@ def test_get_all_events(mocker):
- Make sure that the _time and event_id fields are populated as expected
- Make sure the new_last_run is set.
"""

def json_callback(request, _):
endpoint = request.path.split('/')[-1]
return EVENTS_PAGE_RAW[endpoint]

from NetskopeEventCollector import get_all_events
client = Client(BASE_URL, 'netskope_token', 'v1', validate_certificate=False, proxy=False)
mocker.patch.object(client, 'get_alerts_request_v1', return_value=EVENTS_PAGE_RAW_V1)
mocker.patch.object(client, 'get_events_request_v1', return_value=EVENTS_PAGE_RAW_V1)
events, new_last_run = get_all_events(client, FIRST_LAST_RUN, api_version='v1', limit=6, is_command=False)
client = Client(BASE_URL, 'netskope_token', validate_certificate=False, proxy=False)
url_matcher = re.compile('https://netskope[.]example[.]com/events/dataexport/events')
requests_mock.get(url_matcher, json=json_callback)
events, new_last_run = get_all_events(client, FIRST_LAST_RUN)
assert len(events) == 25
assert events[0].get('event_id') == '3757761212778242bfda29cd'
assert events[0].get('_time') == '2023-05-22T10:30:15.000Z'
assert new_last_run['page'] == 1684751416
assert new_last_run['page-ids'] == ['3757761212778242bfda29cd', '9e99b72b957416a43222fa7a',
'66544bf5fda515f229592644', '98938eb19b4f9bea24ef9a8c',
'fe6d7f3a9a1d4e1abce21713']
assert events[0].get('event_id') == '1'
assert events[0].get('_time') == '2023-05-22T10:30:16.000Z'
assert all([new_last_run[event_type]['operation'] == 'next' for event_type in ALL_SUPPORTED_EVENT_TYPES])


def test_get_events_command(mocker):
Expand All @@ -108,11 +94,72 @@ def test_get_events_command(mocker):
- Make sure the outputs are set correctly.
"""
from NetskopeEventCollector import get_events_command
client = Client(BASE_URL, 'dummy_token', 'v2', False, False)
client = Client(BASE_URL, 'dummy_token', False, False)
mocker.patch('NetskopeEventCollector.get_all_events', return_value=[MOCK_ENTRY, {}])
results, events = get_events_command(client, args={}, last_run=FIRST_LAST_RUN, api_version='v2',
is_command=True)
mocker.patch.object(time, "sleep")
results, events = get_events_command(client, args={}, last_run=FIRST_LAST_RUN)
assert 'Events List' in results.readable_output
assert len(events) == 9
assert results.outputs_prefix == 'Netskope.Event'
assert results.outputs == MOCK_ENTRY


@pytest.mark.parametrize('headers, endpoint, expected_sleep', [
({RATE_LIMIT_REMAINING: 1}, 'test_endpoint', None),
({}, 'test_endpoint', None),
({RATE_LIMIT_REMAINING: 0, RATE_LIMIT_RESET: 2}, 'test_endpoint', 2),
({RATE_LIMIT_REMAINING: 0}, 'test_endpoint', 1),
])
def test_honor_rate_limiting(mocker, headers, endpoint, expected_sleep):
"""
Given:
Case a: Netskope response headers with RATE_LIMIT_REMAINING = 1
Case b: Netskope with response headers
Case c: Netskope with response headers RATE_LIMIT_REMAINING = 1 and RATE_LIMIT_RESET = 2
Case c: Netskope with response headers RATE_LIMIT_REMAINING = 0

When:
Checking if sleeping is required

Then:
Case a: validate that there is no sleeping
Case b: validate that there is no sleeping
Case c: validate that we sleep for 2 secs (which is the reset time)
Case c: validate that we sleep for 1 sec (which is the default in case not rest time is given)
"""
time_mock = mocker.patch.object(time, "sleep")
from NetskopeEventCollector import honor_rate_limiting
honor_rate_limiting(headers=headers, endpoint=endpoint)
if expected_sleep:
time_mock.assert_called_once_with(expected_sleep)
else:
time_mock.assert_not_called()


@pytest.mark.parametrize('last_run_dict, expected_operation_value', [
({}, 1672567200),
({'application': {'operation': 'next'},
'alert': {'operation': 'next'},
'page': {'operation': 'next'},
'audit': {'operation': 'next'},
'network': {'operation': 'next'}}, 'next'),
])
def test_setup_last_run(mocker, last_run_dict, expected_operation_value):
"""
Given:
Case a: previous empty last run
Case a: previous last run with operation= 'next' for all event types

When:
Setting the last run values for the current run

Then:
Case a: make sure all event types in last run are saved with operation= 1672567200
Case b: make sure all event types in last run are saved with operation= 'next'

"""
from NetskopeEventCollector import setup_last_run
first_fetch = dateparser.parse('2023-01-01T10:00:00Z')
mocker.patch.object(dateparser, "parse", return_value=first_fetch)
last_run = setup_last_run(last_run_dict)
assert all([val.get('operation') == expected_operation_value for key, val in last_run.items()])
15 changes: 11 additions & 4 deletions Packs/Netskope/Integrations/NetskopeEventCollector/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,31 @@
| --- | --- | --- |
| Server URL | | True |
| API token | | True |
| API Version | The API version to use \(v1 or v2\). | False |
| Trust any certificate (not secure) | | False |
| Use system proxy settings | | False |
| First fetch timestamp (&lt;number&gt; &lt;time unit&gt;, e.g., 12 hours, 7 days) | | False |
| Max events per fetch | The maximum amount of events to retrieve \(up to 30000 events\). | False |
| Max events per fetch | The maximum amount of events to retrieve per each event type. For more information about event types see the help section. | False |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't you want to add the limitations also to the readme?
Under the known limitation section?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad - Missed that.
Do we want to add also the API limitation regarding indexation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Waiting for the Netskope team investigation resolution on this


4. Click **Test** to validate the URLs, token, and connection.

## Fetch Events Limitation
The first fetch is limited to the last 50,000 events (for each event type) due to API limitation.

The collector can handle 10K events per minute on average per each event type

## Commands

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

### netskope-get-events

***
Returns events extracted from SaaS traffic and or logs.


#### Base Command

`netskope-get-events`

#### Input

| **Argument Name** | **Description** | **Required** |
Expand All @@ -42,9 +44,13 @@ Returns events extracted from SaaS traffic and or logs.
#### Context Output

There is no context output for this command.

#### Command example

```!netskope-get-events limit=1```

#### Context Example

```json
{
"Netskope": {
Expand Down Expand Up @@ -265,6 +271,7 @@ There is no context output for this command.
#### Human Readable Output

>### Events List:

>|Id|Timestamp|Type|Access Method|App|Traffic Type|
>|---|---|---|---|---|---|
>| 23a372c433381a6a11798123 | 2022-07-17T23:48:52.000Z | nspolicy | API Connector | Microsoft Office 365 Sharepoint Online | CloudApp |
Expand Down