diff --git a/Solutions/Commvault Security IQ/Data Connectors/AzureFunctionCommvaultSecurityIQ/main.py b/Solutions/Commvault Security IQ/Data Connectors/AzureFunctionCommvaultSecurityIQ/main.py index 2aac639f156..b526130e7aa 100644 --- a/Solutions/Commvault Security IQ/Data Connectors/AzureFunctionCommvaultSecurityIQ/main.py +++ b/Solutions/Commvault Security IQ/Data Connectors/AzureFunctionCommvaultSecurityIQ/main.py @@ -1,4 +1,4 @@ -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone import base64 import hashlib import hmac @@ -20,7 +20,7 @@ blob_name = "timestamp" cs = os.environ.get('ConnectionString') - + customer_id = os.environ.get('AzureSentinelWorkspaceId','') shared_key = os.environ.get('AzureSentinelSharedKey') verify = False @@ -32,7 +32,7 @@ qsdk_token = None headers = { "Content-Type": "application/json", - "Accept": "application/json", + "Accept": "application/json" } job_details_body = { @@ -92,17 +92,14 @@ "paths": [{"path": "/**/*"}], } -@app.function_name(name="AzureFunctionCommvaultSecurityIQ") -@app.schedule(schedule="0 */5 * * * *", arg_name="myTimer", run_on_startup=True, - use_monitor=False) -def myTimer(myTimer: func.TimerRequest) -> None: - global qsdk_token,url - if myTimer.past_due: - logging.info('The timer is past due!') +def main(mytimer: func.TimerRequest) -> None: + global qsdk_token, url + if mytimer.past_due: + logging.info('The timer is past due!') logging.info('Executing Python timer trigger function.') - + pattern = r'https:\/\/([\w\-]+)\.ods\.opinsights\.azure.([a-zA-Z\.]+)$' match = re.match(pattern, str(logAnalyticsUri)) if (not match): @@ -116,25 +113,40 @@ def myTimer(myTimer: func.TimerRequest) -> None: url = "https://" + uri + "/commandcenter/api" secret_name = "access-token" qsdk_token = client.get_secret(secret_name).value - headers["authtoken"] = "QSDK "+qsdk_token - ustring = "/events?level=10&showInfo=false&showMinor=false&showMajor=true&showCritical=false&showAnomalous=true" + headers["authtoken"] = "QSDK " + qsdk_token + ustring = "/events?level=10&showInfo=false&showMinor=false&showMajor=true&showCritical=true&showAnomalous=true" f_url = url + ustring - current_date = datetime.utcnow() + current_date = datetime.now(timezone.utc) to_time = int(current_date.timestamp()) fromtime = read_blob(cs, container_name, blob_name) if fromtime is None: fromtime = int((current_date - timedelta(days=2)).timestamp()) - - logging.info("Starts at: [{}]".format(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))) + logging.info("From Time : [{}] , since the time read from blob is None".format(fromtime)) + else: + fromtime_dt = datetime.fromtimestamp(fromtime, tz=timezone.utc) + time_diff = current_date - fromtime_dt + if time_diff > timedelta(days=2): + updatedfromtime = int((current_date - timedelta(days=2)).timestamp()) + logging.info("From Time : [{}] , since the time read from blob : [{}] is older than 2 days".format(updatedfromtime,fromtime)) + fromtime = updatedfromtime + elif time_diff < timedelta(minutes = 5): + updatedfromtime = int((current_date - timedelta(minutes=5)).timestamp()) + logging.info("From Time : [{}] , since the time read from blob : [{}] is less than 5 minutes".format(updatedfromtime,fromtime)) + fromtime = updatedfromtime + max_fetch = 1000 + headers["pagingInfo"] = f"0,{max_fetch}" + logging.info("Starts at: [{}]".format(datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S"))) event_endpoint = f"{f_url}&fromTime={fromtime}&toTime={to_time}" + logging.info("Event endpoint : [{}]".format(event_endpoint)) response = requests.get(event_endpoint, headers=headers, verify=verify) - + logging.info("Response Status Code : " + str(response.status_code)) if response.status_code == 200: events = response.json() logging.info("Events Data") logging.info(events) data = events.get("commservEvents") - data = [event for event in data if event.get("eventCodeString") in "7:211|7:212|7:293|7:269|14:337|14:338|69:59|7:333|69:60|35:5575"] + data = [event for event in data if + event.get("eventCodeString") in "7:211|7:212|7:293|7:269|14:337|14:338|69:59|7:333|69:60|35:5575"] post_data = [] if data: for event in data: @@ -147,7 +159,7 @@ def myTimer(myTimer: func.TimerRequest) -> None: upload_timestamp_blob(cs, container_name, blob_name, to_time+1) logging.info("Function App Executed") else: - print("No new events found.") + print("No new events found.") else: logging.error("Failed to get events with status code : "+str(response.status_code)) @@ -195,22 +207,22 @@ def get_backup_anomaly(anomaly_id: int) -> str: def define_severity(anomaly_sub_type: str) -> str | None: - """ - Function to get severity from anomaly sub type - - Args: - anomaly_sub_type (str): The sub type of anomaly - - Returns: - str | None: The severity of the anomaly or None if not found """ - - severity = None - if anomaly_sub_type in ("File Type", "Threat Analysis"): - severity = Constants.severity_high - elif anomaly_sub_type == "File Activity": - severity = Constants.severity_info - return severity +Function to get severity from anomaly sub type + +Args: + anomaly_sub_type (str): The sub type of anomaly + +Returns: + str | None: The severity of the anomaly or None if not found +""" + + severity = None + if anomaly_sub_type in ("File Type", "Threat Analysis"): + severity = Constants.severity_high + elif anomaly_sub_type == "File Activity": + severity = Constants.severity_info + return severity def if_zero_set_none(value: str | None | int) -> str | None | int: @@ -365,6 +377,7 @@ def get_job_details(job_id, url, headers): logging.info(data) return None + def get_user_details(client_name): """ Retrieves the user ID and user name associated with a given client name. @@ -511,7 +524,7 @@ def build_signature(date, content_length, method, content_type, resource): Returns: str: The authorization signature """ - + x_headers = 'x-ms-date:' + date string_to_hash = method + "\n" + str(content_length) + "\n" + content_type + "\n" + x_headers + "\n" + resource bytes_to_hash = bytes(string_to_hash, encoding="utf-8") @@ -584,7 +597,7 @@ def gen_chunks_to_object(data, chunksize=100): Yields: _type_: the chunk - """ + """ chunk = [] for index, line in enumerate(data): if (index % chunksize == 0 and index > 0): @@ -636,7 +649,7 @@ def read_blob(connection_string, container_name, blob_name): Returns: int | None: Timestamp or None if not found """ - + try: blob_service_client = BlobServiceClient.from_connection_string(connection_string) blob_client = blob_service_client.get_blob_client(container=container_name, blob=blob_name) @@ -647,11 +660,11 @@ def read_blob(connection_string, container_name, blob_name): timestamp = int(content) logging.info(f"Timestamp read from blob {blob_name}: {timestamp}") return timestamp - + except ResourceNotFoundError: logging.info(f"Blob '{blob_name}' does not exist.") return None - + except Exception as e: logging.error(f"An error occurred: {str(e)}") raise e \ No newline at end of file diff --git a/Solutions/Commvault Security IQ/Data Connectors/CommvaultSecurityIQDataConnector.zip b/Solutions/Commvault Security IQ/Data Connectors/CommvaultSecurityIQDataConnector.zip index 40d6e93809b..4e249c30b1e 100644 Binary files a/Solutions/Commvault Security IQ/Data Connectors/CommvaultSecurityIQDataConnector.zip and b/Solutions/Commvault Security IQ/Data Connectors/CommvaultSecurityIQDataConnector.zip differ diff --git a/Solutions/Commvault Security IQ/Data Connectors/azuredeploy_CommvaultSecurityIQ_FunctionApp.json b/Solutions/Commvault Security IQ/Data Connectors/azuredeploy_CommvaultSecurityIQ_FunctionApp.json index 417372c700d..3de0d477dea 100644 --- a/Solutions/Commvault Security IQ/Data Connectors/azuredeploy_CommvaultSecurityIQ_FunctionApp.json +++ b/Solutions/Commvault Security IQ/Data Connectors/azuredeploy_CommvaultSecurityIQ_FunctionApp.json @@ -160,6 +160,7 @@ "AzureSentinelWorkspaceId": "[parameters('AzureSentinelWorkspaceId')]", "AzureSentinelSharedKey": "[parameters('AzureSentinelSharedKey')]", "ConnectionString": "[parameters('ConnectionString')]", + "AzureWebJobsStorage": "[concat('DefaultEndpointsProtocol=https;AccountName=', toLower(parameters('FunctionName')),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', toLower(parameters('FunctionName'))), '2019-06-01').keys[0].value, ';EndpointSuffix=',toLower(variables('StorageSuffix')))]", "KeyVaultName": "[parameters('KeyVaultName')]", "WEBSITE_RUN_FROM_PACKAGE": "https://aka.ms/sentinel-CommvaultSecurityIQ-functionapp" }