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

Microsoft support of national clouds - Microsoft Defender for Cloud Apps #27705

Merged
merged 45 commits into from
Aug 9, 2023
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
780116f
CIAC-3854 [Email Communication] Add "Create_Incidents" parameter and …
May 16, 2023
dd902f0
gcc support
May 28, 2023
c9ad31d
gcc support
May 28, 2023
738c9fd
fix ruff
Jun 5, 2023
352beac
fix ruff
Jun 5, 2023
81e2473
Apply suggestions from code review
kobymeir Jun 14, 2023
fc2311e
Nightly build XSIAM - search & install packs retry mechanism is broken.
Jun 14, 2023
48449c1
mcas - ut and code convention
Jun 7, 2023
c9cac2c
CIAC-6390 Microsoft Defender for Applications (MCAS/MDA)
Jun 14, 2023
e96f18b
CIAC-6390 Microsoft Defender for Applications (MCAS/MDA)
Jun 14, 2023
d22f49e
mcas
Jun 19, 2023
d529687
lint fixes
Jun 26, 2023
b446e19
adding azure cloud
Jun 26, 2023
3222fce
Merge branch 'master' into microsoft-usgov-support-mcas
kobymeir Jun 26, 2023
f9b737c
adding azure cloud
Jun 26, 2023
9056db6
sourcery
Jun 26, 2023
89c4286
sourcery
Jun 26, 2023
b9ac83e
sourcery
Jun 26, 2023
b1408d4
sourcery
Jun 26, 2023
443309d
sourcery
Jun 26, 2023
7ced59a
pr review
Jul 6, 2023
8c29c0c
pr review
Jul 6, 2023
0f04d95
Merge branch 'master' into microsoft-usgov-support-mcas
kobymeir Jul 6, 2023
118d39e
pr review
Jul 6, 2023
8cddd36
Merge branch 'master' into microsoft-usgov-support-mcas
kobymeir Aug 7, 2023
2a8d855
merge conflicts
Aug 7, 2023
8e0345b
merge conflicts
Aug 7, 2023
5a9a47c
Merge branch 'master' into microsoft-usgov-support-mcas
kobymeir Aug 7, 2023
53b5be5
doc review
Aug 7, 2023
36638ca
Adding support for Microsoft Defender for APi Endpoints in the Micros…
Aug 8, 2023
1d312ea
Merge branch 'master' into microsoft-usgov-support-mcas
kobymeir Aug 8, 2023
c5f539f
Adding support for Microsoft Defender for APi Endpoints in the Micros…
Aug 8, 2023
7eb3c56
fix to api module
Aug 8, 2023
336294f
Added support for Microsoft Defender for Application Endpoints in the…
Aug 8, 2023
d923f8d
Added support for Microsoft Defender for Application Endpoints in the…
Aug 8, 2023
c0dbafd
Added support for Microsoft Defender for Application Endpoints in the…
Aug 8, 2023
39204a3
Merge branch 'master' into microsoft-usgov-support-mcas
kobymeir Aug 8, 2023
557b649
fixing UT
Aug 8, 2023
1e07878
fixing TPB
Aug 8, 2023
623597f
fixing code review issues
Aug 8, 2023
2e57111
fixing code review issues
Aug 8, 2023
3fce5b1
fixing RN issue
Aug 9, 2023
f748434
Merge branch 'master' into microsoft-usgov-support-mcas
kobymeir Aug 9, 2023
e25f29b
Merged master into current branch.
Aug 9, 2023
b0c2c50
Bump pack from version MicrosoftGraphSecurity to 2.1.27.
Aug 9, 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
73 changes: 47 additions & 26 deletions Packs/ApiModules/Scripts/MicrosoftApiModule/MicrosoftApiModule.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ class Resources:

MICROSOFT_DEFENDER_FOR_ENDPOINT_TYPE_CUSTOM = "Custom"
MICROSOFT_DEFENDER_FOR_ENDPOINT_DEFAULT_ENDPOINT_TYPE = "com"
MICROSOFT_DEFENDER_FOR_APPLICATION_TYPE_CUSTOM = "Custom"
kobymeir marked this conversation as resolved.
Show resolved Hide resolved
kobymeir marked this conversation as resolved.
Show resolved Hide resolved


# https://learn.microsoft.com/en-us/microsoft-365/security/defender/api-supported?view=o365-worldwide#endpoint-uris
# https://learn.microsoft.com/en-us/microsoft-365/security/defender-endpoint/gov?view=o365-worldwide#api
Expand Down Expand Up @@ -124,6 +126,19 @@ class Resources:
'dod': 'https://securitycenter.onmicrosoft.us',
}

MICROSOFT_DEFENDER_FOR_APPLICATION_API = {
"com": "https://api.securitycenter.microsoft.com",
"gcc": "https://api-gcc.securitycenter.microsoft.us",
"gcc-high": "https://api-gcc.securitycenter.microsoft.us",
}


MICROSOFT_DEFENDER_FOR_APPLICATION_TYPE = {
"Worldwide": "com",
"US GCC": "gcc",
"US GCC-High": "gcc-high",
}

# Azure Managed Identities
MANAGED_IDENTITIES_TOKEN_URL = 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01'
MANAGED_IDENTITIES_SYSTEM_ASSIGNED = 'SYSTEM_ASSIGNED'
Expand Down Expand Up @@ -568,12 +583,11 @@ def microsoft_defender_for_endpoint_get_base_url(endpoint_type, url, is_gcc=None
if is_gcc: # Backward compatible.
endpoint_type = "US GCC"
log_message_append = f" ,Overriding endpoint to {endpoint_type}, backward compatible."
elif endpoint_type == MICROSOFT_DEFENDER_FOR_ENDPOINT_TYPE_CUSTOM or not endpoint_type:
elif endpoint_type == MICROSOFT_DEFENDER_FOR_ENDPOINT_TYPE_CUSTOM or not endpoint_type and not url:
kobymeir marked this conversation as resolved.
Show resolved Hide resolved
# When the integration was configured before our Azure Cloud support, the value will be None.
if not url:
if endpoint_type == MICROSOFT_DEFENDER_FOR_ENDPOINT_TYPE_CUSTOM:
raise DemistoException("Endpoint type is set to 'Custom' but no URL was provided.")
raise DemistoException("'Endpoint Type' is not set and no URL was provided.")
if endpoint_type == MICROSOFT_DEFENDER_FOR_ENDPOINT_TYPE_CUSTOM:
raise DemistoException("Endpoint type is set to 'Custom' but no URL was provided.")
raise DemistoException("'Endpoint Type' is not set and no URL was provided.")
endpoint_type = MICROSOFT_DEFENDER_FOR_ENDPOINT_TYPE.get(endpoint_type, 'com')
url = url or MICROSOFT_DEFENDER_FOR_ENDPOINT_API[endpoint_type]
demisto.info(f"Using url:{url}, endpoint type:{endpoint_type}{log_message_append}")
Expand Down Expand Up @@ -720,7 +734,8 @@ def __init__(self, tenant_id: str = '',
self.managed_identities_client_id = managed_identities_client_id
self.managed_identities_resource_uri = managed_identities_resource_uri

def is_command_executed_from_integration(self):
@staticmethod
def is_command_executed_from_integration():
ctx = demisto.callingContext.get('context', {})
executed_commands = ctx.get('ExecutedCommands', [{'moduleBrand': 'Scripts'}])

Expand Down Expand Up @@ -756,7 +771,7 @@ def http_request(
}

if headers:
default_headers.update(headers)
default_headers |= headers

if self.timeout:
kwargs['timeout'] = self.timeout
Expand All @@ -768,8 +783,8 @@ def http_request(
response = super()._http_request( # type: ignore[misc]
*args, resp_type="response", headers=default_headers, **kwargs)

if should_http_retry_on_rate_limit and self.is_command_executed_from_integration():
self.create_api_metrics(response.status_code)
if should_http_retry_on_rate_limit and MicrosoftClient.is_command_executed_from_integration():
MicrosoftClient.create_api_metrics(response.status_code)
# 206 indicates Partial Content, reason will be in the warning header.
# In that case, logs with the warning header will be written.
if response.status_code == 206:
Expand Down Expand Up @@ -802,7 +817,7 @@ def http_request(
else:
demisto.info(f'Scheduling command {demisto.command()}')
command_args['ran_once_flag'] = True
return_results(self.run_retry_on_rate_limit(command_args))
return_results(MicrosoftClient.run_retry_on_rate_limit(command_args))
sys.exit(0)

try:
Expand Down Expand Up @@ -831,8 +846,8 @@ def get_access_token(self, resource: str = '', scope: str | None = None) -> str:
integration context.

Args:
resource (str): The resource identifier for which the generated token will have access to.
scope (str): A scope to get instead of the default on the API.
resource: The resource identifier for which the generated token will have access to.
scope: A scope to get instead of the default on the API.

Returns:
str: Access token that will be added to authorization header.
Expand All @@ -852,10 +867,15 @@ def get_access_token(self, resource: str = '', scope: str | None = None) -> str:

if self.auth_type == OPROXY_AUTH_TYPE:
if self.multi_resource:
expires_in = None
for resource_str in self.resources:
access_token, expires_in, refresh_token = self._oproxy_authorize(resource_str)
access_token, current_expires_in, refresh_token = self._oproxy_authorize(resource_str)
self.resource_to_access_token[resource_str] = access_token
self.refresh_token = refresh_token
expires_in = current_expires_in if expires_in is None else \
min(expires_in, current_expires_in) # type: ignore[call-overload]
if expires_in is None:
raise DemistoException("No resource was provided to get access token from")
else:
access_token, expires_in, refresh_token = self._oproxy_authorize(scope=scope)

Expand Down Expand Up @@ -988,14 +1008,13 @@ def _get_self_deployed_token(self,
if self.grant_type == AUTHORIZATION_CODE:
if not self.multi_resource:
return self._get_self_deployed_token_auth_code(refresh_token, scope=scope)
else:
expires_in = -1 # init variable as an int
for resource in self.resources:
access_token, expires_in, refresh_token = self._get_self_deployed_token_auth_code(refresh_token,
resource)
self.resource_to_access_token[resource] = access_token
expires_in = -1 # init variable as an int
for resource in self.resources:
access_token, expires_in, refresh_token = self._get_self_deployed_token_auth_code(refresh_token,
resource)
self.resource_to_access_token[resource] = access_token

return '', expires_in, refresh_token
return '', expires_in, refresh_token
elif self.grant_type == DEVICE_CODE:
return self._get_token_device_code(refresh_token, scope, integration_context)
else:
Expand Down Expand Up @@ -1033,7 +1052,7 @@ def _get_self_deployed_token_client_credentials(self, scope: str | None = None,

# Set scope.
if self.scope or scope:
data['scope'] = scope if scope else self.scope
data['scope'] = scope or self.scope

if self.resource or resource:
data['resource'] = resource or self.resource # type: ignore
Expand Down Expand Up @@ -1177,16 +1196,18 @@ def _get_refresh_token_from_auth_code_param(self) -> str:
return self.auth_code[len(refresh_prefix):]
return ''

def run_retry_on_rate_limit(self, args_for_next_run: dict):
@staticmethod
def run_retry_on_rate_limit(args_for_next_run: dict):
return CommandResults(readable_output="Rate limit reached, rerunning the command in 1 min",
scheduled_command=ScheduledCommand(command=demisto.command(), next_run_in_seconds=60,
args=args_for_next_run))

def handle_error_with_metrics(self, res):
self.create_api_metrics(res.status_code)
MicrosoftClient.create_api_metrics(res.status_code)
self.client_error_handler(res)

def create_api_metrics(self, status_code):
@staticmethod
def create_api_metrics(status_code):
execution_metrics = ExecutionMetrics()
ok_codes = (200, 201, 202, 204, 206)

Expand Down Expand Up @@ -1262,14 +1283,14 @@ def epoch_seconds(d: datetime = None) -> int:
"""
if not d:
d = MicrosoftClient._get_utcnow()
return int((d - MicrosoftClient._get_utcfromtimestamp(0)).total_seconds())
return int((d - MicrosoftClient._get_utc_from_timestamp(0)).total_seconds())

@staticmethod
def _get_utcnow() -> datetime:
return datetime.utcnow()

@staticmethod
def _get_utcfromtimestamp(_time) -> datetime:
def _get_utc_from_timestamp(_time) -> datetime:
return datetime.utcfromtimestamp(_time)

@staticmethod
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ def test_page_not_found_error(mocker):

def test_epoch_seconds(mocker):
mocker.patch.object(MicrosoftClient, '_get_utcnow', return_value=datetime.datetime(2019, 12, 24, 14, 12, 0, 586636))
mocker.patch.object(MicrosoftClient, '_get_utcfromtimestamp', return_value=datetime.datetime(1970, 1, 1, 0, 0))
mocker.patch.object(MicrosoftClient, '_get_utc_from_timestamp', return_value=datetime.datetime(1970, 1, 1, 0, 0))
integer = MicrosoftClient.epoch_seconds()
assert integer == 1577196720

Expand Down Expand Up @@ -522,8 +522,7 @@ def test_create_api_metrics(mocker, response, result):
mocker.patch('CommonServerPython.is_demisto_version_ge', return_value=True)
mocker.patch('MicrosoftApiModule.is_demisto_version_ge', return_value=True)
mocker.patch.object(demisto, 'callingContext', {'context': {'ExecutedCommands': [{'moduleBrand': 'msgraph'}]}})
client = retry_on_rate_limit_client(True)
client.create_api_metrics(response)
MicrosoftClient.create_api_metrics(response)

metric_results = demisto.results.call_args_list[0][0][0]
assert metric_results.get('Contents') == 'Metrics reported successfully.'
Expand Down
1 change: 1 addition & 0 deletions Packs/MicrosoftCloudAppSecurity/.secrets-ignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
1.2.3.6
XDM_CONST.OUTCOME_PARTIAL,
"$.title.parameters.technique_id"));
https://invalid-url.com