diff --git a/.script/tests/detectionTemplateSchemaValidation/ValidConnectorIds.json b/.script/tests/detectionTemplateSchemaValidation/ValidConnectorIds.json index a34c36430cc..c9aaddd16ec 100644 --- a/.script/tests/detectionTemplateSchemaValidation/ValidConnectorIds.json +++ b/.script/tests/detectionTemplateSchemaValidation/ValidConnectorIds.json @@ -261,5 +261,6 @@ "IllumioSaaSDataConnector", "CTERA", "Workday", - "SamsungDCDefinition" + "SamsungDCDefinition", + "CommvaultSecurityIQ_CL" ] diff --git a/Solutions/Commvault Security IQ/Analytic Rules/CommvaultSecurityIQ_Alert.yaml b/Solutions/Commvault Security IQ/Analytic Rules/CommvaultSecurityIQ_Alert.yaml index 1dcdb3f5139..fb87168e387 100644 --- a/Solutions/Commvault Security IQ/Analytic Rules/CommvaultSecurityIQ_Alert.yaml +++ b/Solutions/Commvault Security IQ/Analytic Rules/CommvaultSecurityIQ_Alert.yaml @@ -1,10 +1,13 @@ id: 317e757e-c320-448e-8837-fc61a70fe609 -name: CommvaultSecurityIQ Alert +name: Commvault Cloud Alert description: | - 'This query identifies CommvaultSecurityIQ Alerts.' + 'This query identifies Alerts from Commvault Cloud.' severity: Medium status: Available -requiredDataConnectors: [] +requiredDataConnectors: + - connectorId: CommvaultSecurityIQ_CL + datatypes: + - CommvaultSecurityIQ_CL queryFrequency: 5m queryPeriod: 5m triggerOperator: gt @@ -25,5 +28,5 @@ query: | CommvaultSecurityIQ_CL | take 1000 entityMappings: null -version: 1.0.0 +version: 1.0.2 kind: Scheduled diff --git a/Solutions/Commvault Security IQ/Analytic Rules/Data_Alert.yaml b/Solutions/Commvault Security IQ/Analytic Rules/Data_Alert.yaml index d60c63c3b9a..07888b99343 100644 --- a/Solutions/Commvault Security IQ/Analytic Rules/Data_Alert.yaml +++ b/Solutions/Commvault Security IQ/Analytic Rules/Data_Alert.yaml @@ -4,7 +4,10 @@ description: | 'This query identifies clients or servers whose data has been compromised.' severity: Medium status: Available -requiredDataConnectors: [] +requiredDataConnectors: + - connectorId: CommvaultSecurityIQ_CL + datatypes: + - CommvaultSecurityIQ_CL queryFrequency: 5m queryPeriod: 5m triggerOperator: gt @@ -27,5 +30,5 @@ query: | | extend extracted_word = extract("Client\\s(.*?)\\sCompromised", 1, Description) | project TimeGenerated, Title, Description, Status entityMappings: null -version: 1.0.0 +version: 1.0.1 kind: Scheduled diff --git a/Solutions/Commvault Security IQ/Analytic Rules/IDP_Alert.yaml b/Solutions/Commvault Security IQ/Analytic Rules/IDP_Alert.yaml index 9731f6f85ae..2847a62675c 100644 --- a/Solutions/Commvault Security IQ/Analytic Rules/IDP_Alert.yaml +++ b/Solutions/Commvault Security IQ/Analytic Rules/IDP_Alert.yaml @@ -4,7 +4,10 @@ description: | 'This query identifies indications of a potential security breach or unauthorized access to the systems and data of the Identity Provider.' severity: Medium status: Available -requiredDataConnectors: [] +requiredDataConnectors: + - connectorId: CommvaultSecurityIQ_CL + datatypes: + - CommvaultSecurityIQ_CL queryFrequency: 5m queryPeriod: 5m triggerOperator: gt @@ -24,5 +27,5 @@ query: | SecurityIncident | where Title has "Cvlt Alert" and Description == "IDP Compromised" and Status has "New" entityMappings: null -version: 1.0.0 +version: 1.0.1 kind: Scheduled diff --git a/Solutions/Commvault Security IQ/Analytic Rules/User_Alert.yaml b/Solutions/Commvault Security IQ/Analytic Rules/User_Alert.yaml index 0b360719c7a..17dfef00e0e 100644 --- a/Solutions/Commvault Security IQ/Analytic Rules/User_Alert.yaml +++ b/Solutions/Commvault Security IQ/Analytic Rules/User_Alert.yaml @@ -4,7 +4,10 @@ description: | 'This query identifies users whose user account or credentials have been compromised.' severity: Medium status: Available -requiredDataConnectors: [] +requiredDataConnectors: + - connectorId: CommvaultSecurityIQ_CL + datatypes: + - CommvaultSecurityIQ_CL queryFrequency: 5m queryPeriod: 5m triggerOperator: gt @@ -26,5 +29,5 @@ query: | | extend extracted_word = extract("User\\s(.*?)\\sCompromised", 1, Description) | project TimeGenerated, Title, Description, Status entityMappings: null -version: 1.0.0 +version: 1.0.1 kind: Scheduled diff --git a/Solutions/Commvault Security IQ/Data Connectors/AzureFunctionCommvaultSecurityIQ/main.py b/Solutions/Commvault Security IQ/Data Connectors/AzureFunctionCommvaultSecurityIQ/main.py index 1a82b242c66..994dc582a57 100644 --- a/Solutions/Commvault Security IQ/Data Connectors/AzureFunctionCommvaultSecurityIQ/main.py +++ b/Solutions/Commvault Security IQ/Data Connectors/AzureFunctionCommvaultSecurityIQ/main.py @@ -20,10 +20,11 @@ blob_name = "timestamp" cs = os.environ.get('AzureWebJobsStorage') +backfill_days = int(os.environ.get('NumberOfDaysToBackfill', "2")) # this is just for testing customer_id = os.environ.get('AzureSentinelWorkspaceId','') shared_key = os.environ.get('AzureSentinelSharedKey') -verify = False + logAnalyticsUri = 'https://' + customer_id + '.ods.opinsights.azure.com' key_vault_name = os.environ.get("KeyVaultName","Commvault-Integration-KV") @@ -114,19 +115,36 @@ def main(mytimer: func.TimerRequest) -> None: secret_name = "access-token" qsdk_token = client.get_secret(secret_name).value headers["authtoken"] = "QSDK " + qsdk_token + + companyId_url = f"{url}/v2/WhoAmI" + company_response = requests.get(companyId_url, headers=headers) + if company_response.status_code == 200: + company_data_json = company_response.json() + logging.info(f"Company Response : {company_data_json}") + company_data = company_data_json.get("company", {}) + companyId = company_data.get("id") + audit_url = f"{url}/V4/Company/{companyId}/SecurityPartners/Register/6" + logging.info(f"Company Id : {companyId}") + audit_response = requests.put(audit_url, headers=headers) + if audit_response.status_code == 200: + logging.info(f"Audit Log request sent Successfully. Audit Response : {audit_response.json()}" ) + else: + logging.error(f"Failed to send Audit Log request with status code : {audit_response.status_code}") + else: + logging.error(f"Failed to get Company Id with status code : {company_response.status_code}") ustring = "/events?level=10&showInfo=false&showMinor=false&showMajor=true&showCritical=true&showAnomalous=true" f_url = url + ustring 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()) + fromtime = int((current_date - timedelta(days=backfill_days)).timestamp()) 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()) + if time_diff > timedelta(days=backfill_days): + updatedfromtime = int((current_date - timedelta(days=backfill_days)).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): @@ -138,8 +156,9 @@ def main(mytimer: func.TimerRequest) -> None: 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) + response = requests.get(event_endpoint, headers=headers) logging.info("Response Status Code : " + str(response.status_code)) + if response.status_code == 200: events = response.json() logging.info("Events Data") @@ -151,7 +170,8 @@ def main(mytimer: func.TimerRequest) -> None: if data: for event in data: temp = get_incident_details(event["description"]) - post_data.append(temp) + if temp: + post_data.append(temp) logging.info("Trying Post Data") gen_chunks(post_data) logging.info("Job Succeeded") @@ -292,7 +312,7 @@ def get_files_list(job_id) -> list: "advConfig": {"browseAdvancedConfigBrowseByJob": {"jobId": int(job_id)}} } f_url = url+"/DoBrowse" - response = requests.post(f_url, headers=headers, json=job_details_body, verify=verify) + response = requests.post(f_url, headers=headers, json=job_details_body) resp = response.json() browse_responses = resp.get("browseResponses", []) file_list = [] @@ -322,7 +342,7 @@ def get_subclient_content_list(subclient_id) -> dict: """ f_url = url + "/Subclient/" + str(subclient_id) - resp = requests.get(f_url, headers=headers, verify=verify).json() + resp = requests.get(f_url, headers=headers).json() resp = resp.get("subClientProperties", [{}])[0].get("content") return resp @@ -364,7 +384,7 @@ def get_job_details(job_id, url, headers): """ f_url = f"{url}/Job/{job_id}" - response = requests.get(f_url, headers=headers, verify=verify) + response = requests.get(f_url, headers=headers) data = response.json() if ("totalRecordsWithoutPaging" in data) and ( int(data["totalRecordsWithoutPaging"]) > 0 @@ -390,9 +410,9 @@ def get_user_details(client_name): """ f_url = f"{url}/Client/byName(clientName='{client_name}')" - response = requests.get(f_url, headers=headers, verify=False).json() - user_id = response['clientProperties'][0]['clientProps']['securityAssociations']['associations'][0]['userOrGroup'][0]['userId'] - user_name = response['clientProperties'][0]['clientProps']['securityAssociations']['associations'][0]['userOrGroup'][0]['userName'] + response = requests.get(f_url, headers=headers).json() + user_id = response.get('clientProperties', [{}])[0].get('clientProps', {}).get('securityAssociations', {}).get('associations', [{}])[0].get('userOrGroup', [{}])[0].get('userId') + user_name = response.get('clientProperties', [{}])[0].get('clientProps', {}).get('securityAssociations', {}).get('associations', [{}])[0].get('userOrGroup', [{}])[0].get('userName') return user_id, user_name @@ -406,108 +426,112 @@ def get_incident_details(message: str) -> dict | None: Returns: dict | None: Incident details or None if not found """ - anomaly_sub_type = extract_from_regex( - message, - "0", - rf"{Constants.anomaly_sub_type}:\[(.*?)\]", - ) - if anomaly_sub_type is None or anomaly_sub_type == "0": - return None - anomaly_sub_type = get_backup_anomaly(int(anomaly_sub_type)) - job_id = extract_from_regex( - message, - "0", - rf"{Constants.job_id}:\[(.*?)\]", - ) - - description = format_alert_description(message) - - job_details = get_job_details(job_id,url,headers) - if job_details is None: - print(f"Invalid job [{job_id}]") + try: + anomaly_sub_type = extract_from_regex( + message, + "0", + rf"{Constants.anomaly_sub_type}:\[(.*?)\]", + ) + if anomaly_sub_type is None or anomaly_sub_type == "0": + return None + anomaly_sub_type = get_backup_anomaly(int(anomaly_sub_type)) + job_id = extract_from_regex( + message, + "0", + rf"{Constants.job_id}:\[(.*?)\]", + ) + + description = format_alert_description(message) + + job_details = get_job_details(job_id,url,headers) + if job_details is None: + print(f"Invalid job [{job_id}]") + return None + job_start_time = int( + job_details.get("jobs", [{}])[0].get("jobSummary", {}).get("jobStartTime") + ) + job_end_time = int( + job_details.get("jobs", [{}])[0].get("jobSummary", {}).get("jobEndTime") + ) + subclient_id = ( + job_details.get("jobs", [{}])[0] + .get("jobSummary", {}) + .get("subclient", {}) + .get("subclientId") + ) + files_list, scanned_folder_list = fetch_file_details(job_id, subclient_id) + originating_client = extract_from_regex(message, "", r"{}:\[(.*?)\]".format(Constants.originating_client)) + user_id, username = get_user_details(originating_client) + details = { + "subclient_id": subclient_id, + "files_list": files_list, + "scanned_folder_list": scanned_folder_list, + "anomaly_sub_type": anomaly_sub_type, + "severity": define_severity(anomaly_sub_type), + "originating_client": originating_client, + "user_id": user_id, + "username": username, + "affected_files_count": if_zero_set_none( + extract_from_regex( + message, + None, + r"{}:\[(.*?)\]".format( + Constants.affected_files_count + ), + ) + ), + "modified_files_count": if_zero_set_none( + extract_from_regex( + message, + None, + r"{}:\[(.*?)\]".format( + Constants.modified_files_count + ), + ) + ), + "deleted_files_count": if_zero_set_none( + extract_from_regex( + message, + None, + r"{}:\[(.*?)\]".format( + Constants.deleted_files_count + ), + ) + ), + "renamed_files_count": if_zero_set_none( + extract_from_regex( + message, + None, + r"{}:\[(.*?)\]".format( + Constants.renamed_files_count + ), + ) + ), + "created_files_count": if_zero_set_none( + extract_from_regex( + message, + None, + r"{}:\[(.*?)\]".format( + Constants.created_files_count + ), + ) + ), + "job_start_time": datetime.utcfromtimestamp(job_start_time).strftime( + "%Y-%m-%d %H:%M:%S" + ), + "job_end_time": datetime.utcfromtimestamp(job_end_time).strftime( + "%Y-%m-%d %H:%M:%S" + ), + "job_id": job_id, + "external_link": extract_from_regex( + message, "", "href='(.*?)'", 'href="(.*?)"' + ), + "description": description, + } + return details + except: + logging.error(f"An error occurred") return None - job_start_time = int( - job_details.get("jobs", [{}])[0].get("jobSummary", {}).get("jobStartTime") - ) - job_end_time = int( - job_details.get("jobs", [{}])[0].get("jobSummary", {}).get("jobEndTime") - ) - subclient_id = ( - job_details.get("jobs", [{}])[0] - .get("jobSummary", {}) - .get("subclient", {}) - .get("subclientId") - ) - files_list, scanned_folder_list = fetch_file_details(job_id, subclient_id) - originating_client = extract_from_regex(message, "", r"{}:\[(.*?)\]".format(Constants.originating_client)) - user_id, username = get_user_details(originating_client) - details = { - "subclient_id": subclient_id, - "files_list": files_list, - "scanned_folder_list": scanned_folder_list, - "anomaly_sub_type": anomaly_sub_type, - "severity": define_severity(anomaly_sub_type), - "originating_client": originating_client, - "user_id": user_id, - "username": username, - "affected_files_count": if_zero_set_none( - extract_from_regex( - message, - None, - r"{}:\[(.*?)\]".format( - Constants.affected_files_count - ), - ) - ), - "modified_files_count": if_zero_set_none( - extract_from_regex( - message, - None, - r"{}:\[(.*?)\]".format( - Constants.modified_files_count - ), - ) - ), - "deleted_files_count": if_zero_set_none( - extract_from_regex( - message, - None, - r"{}:\[(.*?)\]".format( - Constants.deleted_files_count - ), - ) - ), - "renamed_files_count": if_zero_set_none( - extract_from_regex( - message, - None, - r"{}:\[(.*?)\]".format( - Constants.renamed_files_count - ), - ) - ), - "created_files_count": if_zero_set_none( - extract_from_regex( - message, - None, - r"{}:\[(.*?)\]".format( - Constants.created_files_count - ), - ) - ), - "job_start_time": datetime.utcfromtimestamp(job_start_time).strftime( - "%Y-%m-%d %H:%M:%S" - ), - "job_end_time": datetime.utcfromtimestamp(job_end_time).strftime( - "%Y-%m-%d %H:%M:%S" - ), - "job_id": job_id, - "external_link": extract_from_regex( - message, "", "href='(.*?)'", 'href="(.*?)"' - ), - "description": description, - } - return details def build_signature(date, content_length, method, content_type, resource): @@ -625,8 +649,11 @@ def upload_timestamp_blob(connection_string, container_name, blob_name, timestam timestamp_str = str(timestamp) blob_service_client = BlobServiceClient.from_connection_string(connection_string) - + container_client = blob_service_client.get_container_client(container_name) + + if not container_client.exists(): + container_client.create_container() blob_client = container_client.get_blob_client(blob_name) @@ -667,4 +694,4 @@ def read_blob(connection_string, container_name, blob_name): except Exception as e: logging.error(f"An error occurred: {str(e)}") - raise 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 84d8897ce73..77376d894c2 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/CommvaultSecurityIQ_API_AzureFunctionApp.json b/Solutions/Commvault Security IQ/Data Connectors/CommvaultSecurityIQ_API_AzureFunctionApp.json index 815867ed2e3..c9ff0f1cd3e 100644 --- a/Solutions/Commvault Security IQ/Data Connectors/CommvaultSecurityIQ_API_AzureFunctionApp.json +++ b/Solutions/Commvault Security IQ/Data Connectors/CommvaultSecurityIQ_API_AzureFunctionApp.json @@ -111,7 +111,7 @@ }, { "title": "", - "description": "**Option 1 - Azure Resource Manager (ARM) Template**\n\nUse this method for automated deployment of the Commvault Security IQ data connector.\n\n1. Click the **Deploy to Azure** button below. \n\n\t[![Deploy To Azure](https://aka.ms/deploytoazurebutton)](https://aka.ms/sentinel-CommvaultSecurityIQ-azuredeploy)\n2. Select the preferred **Subscription**, **Resource Group** and **Location**. \n3. Enter the **Workspace ID**, **Workspace Key**, **API Username**, **API Password**, 'and/or Other required fields'. \n>Note: If using Azure Key Vault secrets for any of the values above, use the`@Microsoft.KeyVault(SecretUri={Security Identifier})`schema in place of the string values. Refer to [Key Vault references documentation](https://docs.microsoft.com/azure/app-service/app-service-key-vault-references) for further details. \n4. Mark the checkbox labeled **I agree to the terms and conditions stated above**. \n5. Click **Purchase** to deploy." + "description": "**Option 1 - Azure Resource Manager (ARM) Template**\n\nUse this method for automated deployment of the Commvault Security IQ data connector.\n\n1. Click the **Deploy to Azure** button below. \n\n\t[![Deploy To Azure](https://aka.ms/deploytoazurebutton)](https://aka.ms/sentinel-CommvaultSecurityIQ-azuredeploy)\n2. Select the preferred **Subscription**, **Resource Group** and **Location**. \n3. Enter the **Workspace ID**, **Workspace Key** 'and/or Other required fields'. \n>Note: If using Azure Key Vault secrets for any of the values above, use the`@Microsoft.KeyVault(SecretUri={Security Identifier})`schema in place of the string values. Refer to [Key Vault references documentation](https://docs.microsoft.com/azure/app-service/app-service-key-vault-references) for further details. \n4. Mark the checkbox labeled **I agree to the terms and conditions stated above**. \n5. Click **Purchase** to deploy." }, { "title": "", 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 d5b60243218..f55052bb112 100644 --- a/Solutions/Commvault Security IQ/Data Connectors/azuredeploy_CommvaultSecurityIQ_FunctionApp.json +++ b/Solutions/Commvault Security IQ/Data Connectors/azuredeploy_CommvaultSecurityIQ_FunctionApp.json @@ -3,7 +3,7 @@ "contentVersion": "1.0.0.0", "parameters": { "FunctionName": { - "defaultValue": "CommvaultSecurityIQ", + "defaultValue": "CommvaultCloud", "minLength": 1, "type": "string" }, @@ -28,6 +28,7 @@ }, "variables": { "FunctionName": "[concat(toLower(parameters('FunctionName')), uniqueString(resourceGroup().id))]", + "StorageAccountName":"[substring(variables('FunctionName'), 0, 22)]", "StorageSuffix": "[environment().suffixes.storage]", "LogAnaltyicsUri": "[replace(environment().portal, 'https://portal', concat('https://', toLower(parameters('AzureSentinelWorkspaceId')), '.ods.opinsights'))]" }, @@ -47,7 +48,7 @@ { "type": "Microsoft.Storage/storageAccounts", "apiVersion": "2019-06-01", - "name": "[tolower(variables('FunctionName'))]", + "name": "[tolower(variables('StorageAccountName'))]", "location": "[resourceGroup().location]", "sku": { "name": "Standard_LRS", @@ -80,9 +81,9 @@ { "type": "Microsoft.Storage/storageAccounts/blobServices", "apiVersion": "2019-06-01", - "name": "[concat(variables('FunctionName'), '/default')]", + "name": "[concat(variables('StorageAccountName'), '/default')]", "dependsOn": [ - "[resourceId('Microsoft.Storage/storageAccounts', tolower(variables('FunctionName')))]" + "[resourceId('Microsoft.Storage/storageAccounts', tolower(variables('StorageAccountName')))]" ], "sku": { "name": "Standard_LRS", @@ -100,9 +101,9 @@ { "type": "Microsoft.Storage/storageAccounts/fileServices", "apiVersion": "2019-06-01", - "name": "[concat(variables('FunctionName'), '/default')]", + "name": "[concat(variables('StorageAccountName'), '/default')]", "dependsOn": [ - "[resourceId('Microsoft.Storage/storageAccounts', tolower(variables('FunctionName')))]" + "[resourceId('Microsoft.Storage/storageAccounts', tolower(variables('StorageAccountName')))]" ], "sku": { "name": "Standard_LRS", @@ -120,7 +121,7 @@ "name": "[variables('FunctionName')]", "location": "[resourceGroup().location]", "dependsOn": [ - "[resourceId('Microsoft.Storage/storageAccounts', tolower(variables('FunctionName')))]", + "[resourceId('Microsoft.Storage/storageAccounts', tolower(variables('StorageAccountName')))]", "[resourceId('Microsoft.Insights/components', variables('FunctionName'))]" ], "kind": "functionapp,linux", @@ -147,14 +148,13 @@ "[concat('Microsoft.Web/sites/', variables('FunctionName'))]" ], "properties": { - "FUNCTIONS_EXTENSION_VERSION": "~4", "FUNCTIONS_WORKER_RUNTIME": "python", "APPINSIGHTS_INSTRUMENTATIONKEY": "[reference(resourceId('Microsoft.insights/components', variables('FunctionName')), '2015-05-01').InstrumentationKey]", "APPLICATIONINSIGHTS_CONNECTION_STRING": "[reference(resourceId('microsoft.insights/components', variables('FunctionName')), '2015-05-01').ConnectionString]", "AzureSentinelWorkspaceId": "[parameters('AzureSentinelWorkspaceId')]", "AzureSentinelSharedKey": "[parameters('AzureSentinelSharedKey')]", - "AzureWebJobsStorage": "[concat('DefaultEndpointsProtocol=https;AccountName=', toLower(variables('FunctionName')),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', toLower(variables('FunctionName'))), '2019-06-01').keys[0].value, ';EndpointSuffix=',toLower(variables('StorageSuffix')))]", + "AzureWebJobsStorage": "[concat('DefaultEndpointsProtocol=https;AccountName=', toLower(variables('StorageAccountName')),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', toLower(variables('StorageAccountName'))), '2019-06-01').keys[0].value, ';EndpointSuffix=',toLower(variables('StorageSuffix')))]", "KeyVaultName": "[parameters('KeyVaultName')]", "WEBSITE_RUN_FROM_PACKAGE": "https://aka.ms/sentinel-CommvaultSecurityIQ-functionapp" } @@ -164,10 +164,10 @@ { "type": "Microsoft.Storage/storageAccounts/blobServices/containers", "apiVersion": "2019-06-01", - "name": "[concat(variables('FunctionName'), '/default/azure-webjobs-hosts')]", + "name": "[concat(variables('StorageAccountName'), '/default/azure-webjobs-hosts')]", "dependsOn": [ - "[resourceId('Microsoft.Storage/storageAccounts/blobServices', variables('FunctionName'), 'default')]", - "[resourceId('Microsoft.Storage/storageAccounts', variables('FunctionName'))]" + "[resourceId('Microsoft.Storage/storageAccounts/blobServices', variables('StorageAccountName'), 'default')]", + "[resourceId('Microsoft.Storage/storageAccounts', variables('StorageAccountName'))]" ], "properties": { "publicAccess": "None" @@ -176,10 +176,10 @@ { "type": "Microsoft.Storage/storageAccounts/blobServices/containers", "apiVersion": "2019-06-01", - "name": "[concat(variables('FunctionName'), '/default/azure-webjobs-secrets')]", + "name": "[concat(variables('StorageAccountName'), '/default/azure-webjobs-secrets')]", "dependsOn": [ - "[resourceId('Microsoft.Storage/storageAccounts/blobServices', variables('FunctionName'), 'default')]", - "[resourceId('Microsoft.Storage/storageAccounts', variables('FunctionName'))]" + "[resourceId('Microsoft.Storage/storageAccounts/blobServices', variables('StorageAccountName'), 'default')]", + "[resourceId('Microsoft.Storage/storageAccounts', variables('StorageAccountName'))]" ], "properties": { "publicAccess": "None" @@ -188,10 +188,10 @@ { "type": "Microsoft.Storage/storageAccounts/fileServices/shares", "apiVersion": "2019-06-01", - "name": "[concat(variables('FunctionName'), '/default/', tolower(variables('FunctionName')))]", + "name": "[concat(variables('StorageAccountName'), '/default/', tolower(variables('StorageAccountName')))]", "dependsOn": [ - "[resourceId('Microsoft.Storage/storageAccounts/fileServices', variables('FunctionName'), 'default')]", - "[resourceId('Microsoft.Storage/storageAccounts', variables('FunctionName'))]" + "[resourceId('Microsoft.Storage/storageAccounts/fileServices', variables('StorageAccountName'), 'default')]", + "[resourceId('Microsoft.Storage/storageAccounts', variables('StorageAccountName'))]" ], "properties": { "shareQuota": 5120 diff --git a/Solutions/Commvault Security IQ/Data/Solution_Commvault Security IQ.json b/Solutions/Commvault Security IQ/Data/Solution_Commvault Security IQ.json index 5a927343e88..11b2c888ff3 100644 --- a/Solutions/Commvault Security IQ/Data/Solution_Commvault Security IQ.json +++ b/Solutions/Commvault Security IQ/Data/Solution_Commvault Security IQ.json @@ -19,7 +19,7 @@ ], "Metadata": "SolutionMetadata.json", "BasePath": "C:\\GitHub\\Azure-Sentinel\\Solutions\\Commvault Security IQ", - "Version": "3.0.1", + "Version": "3.0.2", "TemplateSpec": true, "Is1Pconnector": false -} \ No newline at end of file +} diff --git a/Solutions/Commvault Security IQ/Package/3.0.2.zip b/Solutions/Commvault Security IQ/Package/3.0.2.zip new file mode 100644 index 00000000000..7adfd308d0b Binary files /dev/null and b/Solutions/Commvault Security IQ/Package/3.0.2.zip differ diff --git a/Solutions/Commvault Security IQ/Package/createUiDefinition.json b/Solutions/Commvault Security IQ/Package/createUiDefinition.json index 32d409dbeff..e099a065819 100644 --- a/Solutions/Commvault Security IQ/Package/createUiDefinition.json +++ b/Solutions/Commvault Security IQ/Package/createUiDefinition.json @@ -64,7 +64,7 @@ } }, { - "name": "dataconnectors-link2", + "name": "dataconnectors-link1", "type": "Microsoft.Common.TextBlock", "options": { "link": { @@ -104,13 +104,13 @@ { "name": "analytic1", "type": "Microsoft.Common.Section", - "label": "CommvaultSecurityIQ Alert", + "label": "Commvault Cloud Alert", "elements": [ { "name": "analytic1-text", "type": "Microsoft.Common.TextBlock", "options": { - "text": "This query identifies CommvaultSecurityIQ Alerts." + "text": "This query identifies Alerts from Commvault Cloud." } } ] diff --git a/Solutions/Commvault Security IQ/Package/mainTemplate.json b/Solutions/Commvault Security IQ/Package/mainTemplate.json index 1be781d80a9..cf69ebbcd23 100644 --- a/Solutions/Commvault Security IQ/Package/mainTemplate.json +++ b/Solutions/Commvault Security IQ/Package/mainTemplate.json @@ -31,36 +31,36 @@ }, "variables": { "_solutionName": "Commvault Security IQ", - "_solutionVersion": "3.0.1", + "_solutionVersion": "3.0.2", "solutionId": "commvault.microsoft-sentinel-solution-commvaultsecurityiq", "_solutionId": "[variables('solutionId')]", "analyticRuleObject1": { - "analyticRuleVersion1": "1.0.0", + "analyticRuleVersion1": "1.0.2", "_analyticRulecontentId1": "317e757e-c320-448e-8837-fc61a70fe609", "analyticRuleId1": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', '317e757e-c320-448e-8837-fc61a70fe609')]", "analyticRuleTemplateSpecName1": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('317e757e-c320-448e-8837-fc61a70fe609')))]", - "_analyticRulecontentProductId1": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','317e757e-c320-448e-8837-fc61a70fe609','-', '1.0.0')))]" + "_analyticRulecontentProductId1": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','317e757e-c320-448e-8837-fc61a70fe609','-', '1.0.2')))]" }, "analyticRuleObject2": { - "analyticRuleVersion2": "1.0.0", + "analyticRuleVersion2": "1.0.1", "_analyticRulecontentId2": "1d2c3da7-60ec-40be-9c14-bade6eaf3c49", "analyticRuleId2": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', '1d2c3da7-60ec-40be-9c14-bade6eaf3c49')]", "analyticRuleTemplateSpecName2": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('1d2c3da7-60ec-40be-9c14-bade6eaf3c49')))]", - "_analyticRulecontentProductId2": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','1d2c3da7-60ec-40be-9c14-bade6eaf3c49','-', '1.0.0')))]" + "_analyticRulecontentProductId2": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','1d2c3da7-60ec-40be-9c14-bade6eaf3c49','-', '1.0.1')))]" }, "analyticRuleObject3": { - "analyticRuleVersion3": "1.0.0", + "analyticRuleVersion3": "1.0.1", "_analyticRulecontentId3": "c982bcc1-ef73-485b-80d5-2a637ce4ab2b", "analyticRuleId3": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'c982bcc1-ef73-485b-80d5-2a637ce4ab2b')]", "analyticRuleTemplateSpecName3": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('c982bcc1-ef73-485b-80d5-2a637ce4ab2b')))]", - "_analyticRulecontentProductId3": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','c982bcc1-ef73-485b-80d5-2a637ce4ab2b','-', '1.0.0')))]" + "_analyticRulecontentProductId3": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','c982bcc1-ef73-485b-80d5-2a637ce4ab2b','-', '1.0.1')))]" }, "analyticRuleObject4": { - "analyticRuleVersion4": "1.0.0", + "analyticRuleVersion4": "1.0.1", "_analyticRulecontentId4": "29e0767c-80ac-4689-9a2e-b25b9fc88fce", "analyticRuleId4": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', '29e0767c-80ac-4689-9a2e-b25b9fc88fce')]", "analyticRuleTemplateSpecName4": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('29e0767c-80ac-4689-9a2e-b25b9fc88fce')))]", - "_analyticRulecontentProductId4": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','29e0767c-80ac-4689-9a2e-b25b9fc88fce','-', '1.0.0')))]" + "_analyticRulecontentProductId4": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','29e0767c-80ac-4689-9a2e-b25b9fc88fce','-', '1.0.1')))]" }, "Commvault_Disable_Data_Aging_Logic_App": "Commvault_Disable_Data_Aging_Logic_App", "_Commvault_Disable_Data_Aging_Logic_App": "[variables('Commvault_Disable_Data_Aging_Logic_App')]", @@ -109,7 +109,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "CommvaultSecurityIQ_Alert_AnalyticalRules Analytics Rule with template version 3.0.1", + "description": "CommvaultSecurityIQ_Alert_AnalyticalRules Analytics Rule with template version 3.0.2", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject1').analyticRuleVersion1]", @@ -123,8 +123,8 @@ "kind": "Scheduled", "location": "[parameters('workspace-location')]", "properties": { - "description": "This query identifies CommvaultSecurityIQ Alerts.", - "displayName": "CommvaultSecurityIQ Alert", + "description": "This query identifies Alerts from Commvault Cloud.", + "displayName": "Commvault Cloud Alert", "enabled": false, "query": "CommvaultSecurityIQ_CL\n| take 1000\n", "queryFrequency": "PT5M", @@ -135,6 +135,14 @@ "triggerOperator": "GreaterThan", "triggerThreshold": 0, "status": "Available", + "requiredDataConnectors": [ + { + "connectorId": "CommvaultSecurityIQ_CL", + "datatypes": [ + "CommvaultSecurityIQ_CL" + ] + } + ], "tactics": [ "DefenseEvasion", "Impact" @@ -180,7 +188,7 @@ "contentSchemaVersion": "3.0.0", "contentId": "[variables('analyticRuleObject1')._analyticRulecontentId1]", "contentKind": "AnalyticsRule", - "displayName": "CommvaultSecurityIQ Alert", + "displayName": "Commvault Cloud Alert", "contentProductId": "[variables('analyticRuleObject1')._analyticRulecontentProductId1]", "id": "[variables('analyticRuleObject1')._analyticRulecontentProductId1]", "version": "[variables('analyticRuleObject1').analyticRuleVersion1]" @@ -195,7 +203,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "Data_Alert_AnalyticalRules Analytics Rule with template version 3.0.1", + "description": "Data_Alert_AnalyticalRules Analytics Rule with template version 3.0.2", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject2').analyticRuleVersion2]", @@ -221,6 +229,14 @@ "triggerOperator": "GreaterThan", "triggerThreshold": 0, "status": "Available", + "requiredDataConnectors": [ + { + "connectorId": "CommvaultSecurityIQ_CL", + "datatypes": [ + "CommvaultSecurityIQ_CL" + ] + } + ], "tactics": [ "DefenseEvasion", "Impact" @@ -281,7 +297,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "IDP_Alert_AnalyticalRules Analytics Rule with template version 3.0.1", + "description": "IDP_Alert_AnalyticalRules Analytics Rule with template version 3.0.2", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject3').analyticRuleVersion3]", @@ -307,6 +323,14 @@ "triggerOperator": "GreaterThan", "triggerThreshold": 0, "status": "Available", + "requiredDataConnectors": [ + { + "connectorId": "CommvaultSecurityIQ_CL", + "datatypes": [ + "CommvaultSecurityIQ_CL" + ] + } + ], "tactics": [ "DefenseEvasion", "Impact" @@ -367,7 +391,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "User_Alert_AnalyticalRules Analytics Rule with template version 3.0.1", + "description": "User_Alert_AnalyticalRules Analytics Rule with template version 3.0.2", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject4').analyticRuleVersion4]", @@ -393,6 +417,14 @@ "triggerOperator": "GreaterThan", "triggerThreshold": 0, "status": "Available", + "requiredDataConnectors": [ + { + "connectorId": "CommvaultSecurityIQ_CL", + "datatypes": [ + "CommvaultSecurityIQ_CL" + ] + } + ], "tactics": [ "DefenseEvasion", "Impact" @@ -453,7 +485,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "logic-app-disable-data-aging Playbook with template version 3.0.1", + "description": "logic-app-disable-data-aging Playbook with template version 3.0.2", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('playbookVersion1')]", @@ -903,7 +935,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "logic-app-disable-saml-provider Playbook with template version 3.0.1", + "description": "logic-app-disable-saml-provider Playbook with template version 3.0.2", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('playbookVersion2')]", @@ -1245,7 +1277,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "logic-app-disable-user Playbook with template version 3.0.1", + "description": "logic-app-disable-user Playbook with template version 3.0.2", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('playbookVersion3')]", @@ -1691,7 +1723,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "Commvault Security IQ data connector with template version 3.0.1", + "description": "Commvault Security IQ data connector with template version 3.0.2", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('dataConnectorVersion1')]", @@ -1813,7 +1845,7 @@ ] }, { - "description": "**Option 1 - Azure Resource Manager (ARM) Template**\n\nUse this method for automated deployment of the Commvault Security IQ data connector.\n\n1. Click the **Deploy to Azure** button below. \n\n\t[![Deploy To Azure](https://aka.ms/deploytoazurebutton)](https://aka.ms/sentinel-CommvaultSecurityIQ-azuredeploy)\n2. Select the preferred **Subscription**, **Resource Group** and **Location**. \n3. Enter the **Workspace ID**, **Workspace Key**, **API Username**, **API Password**, 'and/or Other required fields'. \n>Note: If using Azure Key Vault secrets for any of the values above, use the`@Microsoft.KeyVault(SecretUri={Security Identifier})`schema in place of the string values. Refer to [Key Vault references documentation](https://docs.microsoft.com/azure/app-service/app-service-key-vault-references) for further details. \n4. Mark the checkbox labeled **I agree to the terms and conditions stated above**. \n5. Click **Purchase** to deploy." + "description": "**Option 1 - Azure Resource Manager (ARM) Template**\n\nUse this method for automated deployment of the Commvault Security IQ data connector.\n\n1. Click the **Deploy to Azure** button below. \n\n\t[![Deploy To Azure](https://aka.ms/deploytoazurebutton)](https://aka.ms/sentinel-CommvaultSecurityIQ-azuredeploy)\n2. Select the preferred **Subscription**, **Resource Group** and **Location**. \n3. Enter the **Workspace ID**, **Workspace Key** 'and/or Other required fields'. \n>Note: If using Azure Key Vault secrets for any of the values above, use the`@Microsoft.KeyVault(SecretUri={Security Identifier})`schema in place of the string values. Refer to [Key Vault references documentation](https://docs.microsoft.com/azure/app-service/app-service-key-vault-references) for further details. \n4. Mark the checkbox labeled **I agree to the terms and conditions stated above**. \n5. Click **Purchase** to deploy." }, { "description": "**Option 2 - Manual Deployment of Azure Functions**\n\n Use the following step-by-step instructions to deploy the CommvaultSecurityIQ data connector manually with Azure Functions." @@ -2017,7 +2049,7 @@ ] }, { - "description": "**Option 1 - Azure Resource Manager (ARM) Template**\n\nUse this method for automated deployment of the Commvault Security IQ data connector.\n\n1. Click the **Deploy to Azure** button below. \n\n\t[![Deploy To Azure](https://aka.ms/deploytoazurebutton)](https://aka.ms/sentinel-CommvaultSecurityIQ-azuredeploy)\n2. Select the preferred **Subscription**, **Resource Group** and **Location**. \n3. Enter the **Workspace ID**, **Workspace Key**, **API Username**, **API Password**, 'and/or Other required fields'. \n>Note: If using Azure Key Vault secrets for any of the values above, use the`@Microsoft.KeyVault(SecretUri={Security Identifier})`schema in place of the string values. Refer to [Key Vault references documentation](https://docs.microsoft.com/azure/app-service/app-service-key-vault-references) for further details. \n4. Mark the checkbox labeled **I agree to the terms and conditions stated above**. \n5. Click **Purchase** to deploy." + "description": "**Option 1 - Azure Resource Manager (ARM) Template**\n\nUse this method for automated deployment of the Commvault Security IQ data connector.\n\n1. Click the **Deploy to Azure** button below. \n\n\t[![Deploy To Azure](https://aka.ms/deploytoazurebutton)](https://aka.ms/sentinel-CommvaultSecurityIQ-azuredeploy)\n2. Select the preferred **Subscription**, **Resource Group** and **Location**. \n3. Enter the **Workspace ID**, **Workspace Key** 'and/or Other required fields'. \n>Note: If using Azure Key Vault secrets for any of the values above, use the`@Microsoft.KeyVault(SecretUri={Security Identifier})`schema in place of the string values. Refer to [Key Vault references documentation](https://docs.microsoft.com/azure/app-service/app-service-key-vault-references) for further details. \n4. Mark the checkbox labeled **I agree to the terms and conditions stated above**. \n5. Click **Purchase** to deploy." }, { "description": "**Option 2 - Manual Deployment of Azure Functions**\n\n Use the following step-by-step instructions to deploy the CommvaultSecurityIQ data connector manually with Azure Functions." @@ -2044,7 +2076,7 @@ "apiVersion": "2023-04-01-preview", "location": "[parameters('workspace-location')]", "properties": { - "version": "3.0.1", + "version": "3.0.2", "kind": "Solution", "contentSchemaVersion": "3.0.0", "displayName": "Commvault Security IQ", diff --git a/Solutions/Commvault Security IQ/ReleaseNotes.md b/Solutions/Commvault Security IQ/ReleaseNotes.md index f1fd8a1e752..c650173f57f 100644 --- a/Solutions/Commvault Security IQ/ReleaseNotes.md +++ b/Solutions/Commvault Security IQ/ReleaseNotes.md @@ -1,4 +1,4 @@ | **Version** | **Date Modified (DD-MM-YYYY)** | **Change History** | |-------------|--------------------------------|---------------------------------------------| | 3.0.1 | 28-03-2024 | Adding **Data Connector** for Commvault Sentinel Integration| -| 3.0.0 | 21-08-2023 | Initial Solution Release| \ No newline at end of file +| 3.0.0 | 21-08-2023 | Initial Solution Release|