diff --git a/.azure-pipelines/all.yml b/.azure-pipelines/all.yml index 26cfa44948..f848229cdb 100644 --- a/.azure-pipelines/all.yml +++ b/.azure-pipelines/all.yml @@ -53,6 +53,9 @@ jobs: - checkName: cloudsmith displayName: Cloudsmith os: linux + - checkName: cybersixgill_actionable_alerts + displayName: cybersixgill_actionable_alerts + os: linux - checkName: cyral displayName: Cyral os: linux diff --git a/.codecov.yml b/.codecov.yml index 7957b621ae..39ed87ccf6 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -215,6 +215,10 @@ coverage: target: 75 flags: - cfssl + cybersixgill_actionable_alerts: + target: 75 + flags: + - cybersixgill_actionable_alerts exim: target: 75 flags: @@ -285,6 +289,11 @@ flags: paths: - cloudsmith/datadog_checks/cloudsmith - cloudsmith/tests + cybersixgill_actionable_alerts: + carryforward: true + paths: + - cybersixgill_actionable_alerts/datadog_checks/cybersixgill_actionable_alerts + - cybersixgill_actionable_alerts/tests cyral: carryforward: true paths: diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b752fa07d3..29469203ca 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -40,6 +40,7 @@ /contrastsecurity/ @kristianamitchellcontrastsecurity kristiana.mitchell@contrastsecurity.com /convox/ @DataDog/agent-integrations /cortex/ @cortexapps/engineering support@getcortexapp.com @DataDog/marketplace-review +/cybersixgill_actionable_alerts/ @shahul-loginsoft sshaik@loginsoft.com @DataDog/marketplace-review /cyral/ @tyrannosaurus-becks product@cyral.com @DataDog/marketplace-review /data_runner/ @DataDog/apps-sdk @DataDog/marketplace-review /datazoom/ @DataDog/web-integrations @@ -267,6 +268,9 @@ /cortex/*metadata.csv @cortexapps/engineering support@getcortexapp.com @DataDog/documentation /cortex/manifest.json @cortexapps/engineering support@getcortexapp.com @DataDog/documentation /cortex/README.md @cortexapps/engineering support@getcortexapp.com @DataDog/documentation +/cybersixgill_actionable_alerts/*metadata.csv @shahul-loginsoft sshaik@loginsoft.com @DataDog/documentation @DataDog/marketplace-review +/cybersixgill_actionable_alerts/manifest.json @shahul-loginsoft sshaik@loginsoft.com @DataDog/documentation @DataDog/marketplace-review +/cybersixgill_actionable_alerts/README.md @shahul-loginsoft sshaik@loginsoft.com @DataDog/documentation @DataDog/marketplace-review /cyral/*metadata.csv @tyrannosaurus-becks product@cyral.com @DataDog/documentation /cyral/manifest.json @tyrannosaurus-becks product@cyral.com @DataDog/documentation /cyral/README.md @tyrannosaurus-becks product@cyral.com @DataDog/documentation @@ -363,14 +367,11 @@ /logzio/*metadata.csv @DataDog/agent-integrations @DataDog/documentation /logzio/manifest.json @DataDog/agent-integrations @DataDog/documentation /logzio/README.md @DataDog/agent-integrations @DataDog/documentation -<<<<<<< HEAD /mendix/README.md @mendix/cloud @DataDog/agent-integrations @DataDog/documentation /mendix/manifest.json @mendix/cloud @DataDog/agent-integrations @DataDog/documentation -======= /mergify/*metadata.csv @Mergifyio/oss-integrations @DataDog/documentation /mergify/manifest.json @Mergifyio/oss-integrations @DataDog/documentation /mergify/README.md @Mergifyio/oss-integrations @DataDog/documentation ->>>>>>> bfce30e1 (feat: add mergify integration) /n2ws/*metadata.csv @eliadeini eliad.eini@n2ws.com @DataDog/documentation /n2ws/manifest.json @eliadeini eliad.eini@n2ws.com @DataDog/documentation /n2ws/README.md @eliadeini eliad.eini@n2ws.com @DataDog/documentation diff --git a/.github/workflows/test-all.yml b/.github/workflows/test-all.yml index ec0bb6d744..92a6e4ce82 100644 --- a/.github/workflows/test-all.yml +++ b/.github/workflows/test-all.yml @@ -144,6 +144,25 @@ jobs: test-py3: ${{ inputs.test-py3 }} setup-env-vars: "${{ inputs.setup-env-vars }}" secrets: inherit + ja669dc6: + uses: DataDog/integrations-core/.github/workflows/test-target.yml@master + with: + job-name: cybersixgill_actionable_alerts + target: cybersixgill_actionable_alerts + platform: linux + runner: '["ubuntu-22.04"]' + repo: "${{ inputs.repo }}" + python-version: "${{ inputs.python-version }}" + standard: ${{ inputs.standard }} + latest: ${{ inputs.latest }} + agent-image: "${{ inputs.agent-image }}" + agent-image-py2: "${{ inputs.agent-image-py2 }}" + agent-image-windows: "${{ inputs.agent-image-windows }}" + agent-image-windows-py2: "${{ inputs.agent-image-windows-py2 }}" + test-py2: ${{ inputs.test-py2 }} + test-py3: ${{ inputs.test-py3 }} + setup-env-vars: "${{ inputs.setup-env-vars }}" + secrets: inherit j3263e78: uses: DataDog/integrations-core/.github/workflows/test-target.yml@master with: diff --git a/cybersixgill_actionable_alerts/CHANGELOG.md b/cybersixgill_actionable_alerts/CHANGELOG.md new file mode 100644 index 0000000000..21bcad304b --- /dev/null +++ b/cybersixgill_actionable_alerts/CHANGELOG.md @@ -0,0 +1,5 @@ +# CHANGELOG - cybersixgill_actionable_alerts + +## 1.0.0 / 2023-04-04 + +[FEATURE] Initial Cybersixgill Integration \ No newline at end of file diff --git a/cybersixgill_actionable_alerts/README.md b/cybersixgill_actionable_alerts/README.md new file mode 100644 index 0000000000..0c4bc9be8a --- /dev/null +++ b/cybersixgill_actionable_alerts/README.md @@ -0,0 +1,46 @@ +# Agent Check: cybersixgill_actionable_alerts + +## Overview +The Cybersixgill actionable alerts check monitors critical assets across the deep, dark, and surface web such as IP addresses, domains, vulnerabilities, and VIPs. Receive alerts with context including severity, threat type, description, post snippet, recommendations, and assessments. This integration provides an out-of-the-box dashboard to prioritize and respond to threats. + +## Setup + + +### Installation + +To install the Cybersixgill actionable alerts check on your host: +1. Install the [developer tool][2] on any machine. +2. To build the package, run the command: `ddev release build cybersixgill_actionable_alerts`. +3. [Install the Datadog Agent][1] on your host. +4. Once the Agent is installed, run the following command to install the integration: +``` +datadog-agent integration install -t datadog-Cybersixgill Actionable Alerts==1.0.0 +``` + +### Configuration +5. Reach out to [Cybersixgill Support][4] and request access to the Cybersixgill Developer Platform. +6. Receive the welcome email with access to the Cybersixgill developer platform. +7. Within the Cybersixgill developer platform, create the Client ID and Client secret. +8. Copy the Client ID and Client secret and paste them into the Configuration.yaml file. +9. Provide the minimum collection interval in seconds. For example, `min_collection_interval: 3600` + +### Validation +Verify that Cybersixgill events are generated in the [Datadog Events Explorer][3]. + +## Data Collected + +### Service Checks +See [service_checks.json][5] for a list of service checks provided by this integration. + +### Events +This integration sends API-type events to Datadog. + +## Troubleshooting +Need help? Contact [Cybersixgill support][4]. + +[1]: https://app.datadoghq.com/account/settings#agent +[2]: https://docs.datadoghq.com/developers/integrations/new_check_howto/?tab=configurationtemplate#configure-the-developer-tool +[3]: https://app.datadoghq.com/event/explorer +[4]: mailto:support@cybersixgill.com +[5]: https://github.com/DataDog/integrations-extras/blob/master/cybersixgill_actionable_alerts/assets/service_checks.json + diff --git a/cybersixgill_actionable_alerts/assets/configuration/spec.yaml b/cybersixgill_actionable_alerts/assets/configuration/spec.yaml new file mode 100644 index 0000000000..5c5473f8c0 --- /dev/null +++ b/cybersixgill_actionable_alerts/assets/configuration/spec.yaml @@ -0,0 +1,71 @@ +name: cybersixgill_actionable_alerts +files: +- name: cybersixgill_actionable_alerts.yaml + options: + - template: init_config + options: + - template: init_config/default + - template: instances + options: + - name: cl_id + required: true + description: The Client Id given by Cybersixgill + enabled: true + value: + type: string + example: clientid + display_default: null + - name: cl_secret + required: true + description: The Client Secret given by Cybersixgill + enabled: true + value: + type: string + display_default: null + secret: true + - name: alerts_limit + required: false + description: The number of alerts to fetch on a single request default is 50 + enabled: false + value: + type: integer + example: 50 + display_default: null + - name: threat_type + required: false + description: Predefined types of threats alerts you would like to see like fraud, malware + enabled: false + value: + type: string + example: compromised accounts, fraud + display_default: null + enum: + - Brand Protection + - Data Leak + - Malware + - Phishing + - Fraud + - Vulnerability Exploit + - Insider Threat + - Defacement + - Compromised Accounts + - DDoS Attack + - Web Attack + - Trend Anomaly + - name: threat_level + required: false + description: Type of alerts which are either imminent or emerging + enabled: false + value: + type: string + example: imminent + display_default: null + - name: organization_id + required: false + description: The Organization Id provided by Cybersixgill + enabled: false + value: + type: string + example: orgidexample + display_default: null + - template: instances/default diff --git a/cybersixgill_actionable_alerts/assets/dashboards/cybersixgill_actionable_alerts_overview.json b/cybersixgill_actionable_alerts/assets/dashboards/cybersixgill_actionable_alerts_overview.json new file mode 100644 index 0000000000..7937b25e71 --- /dev/null +++ b/cybersixgill_actionable_alerts/assets/dashboards/cybersixgill_actionable_alerts_overview.json @@ -0,0 +1,180 @@ +{ + "title": "Cybersixgill Actionable Alerts - Overview", + "description": "This Dashboard helps you to understand the Actionable alerts of your assets like the count of alerts, alert titles and count of imminent and emerging alerts.", + "widgets": [ + { + "id": 8775317340528879, + "definition": { + "title": "Alerts Count", + "title_size": "16", + "title_align": "left", + "show_legend": false, + "legend_layout": "auto", + "legend_columns": [ + "avg", + "min", + "max", + "value", + "sum" + ], + "time": {}, + "type": "timeseries", + "requests": [ + { + "formulas": [ + { + "formula": "query1" + } + ], + "response_format": "timeseries", + "queries": [ + { + "search": { + "query": "source:my_apps" + }, + "data_source": "events", + "compute": { + "aggregation": "count" + }, + "name": "query1", + "indexes": [ + "*" + ], + "group_by": [] + } + ], + "style": { + "palette": "dog_classic" + }, + "display_type": "bars" + } + ] + }, + "layout": { + "x": 0, + "y": 0, + "width": 11, + "height": 3 + } + }, + { + "id": 6080537854801146, + "definition": { + "title": "Alerts Title", + "title_size": "16", + "title_align": "left", + "time": {}, + "type": "event_stream", + "query": "source: my_apps", + "event_size": "s" + }, + "layout": { + "x": 0, + "y": 3, + "width": 11, + "height": 5 + } + }, + { + "id": 2249705270211652, + "definition": { + "title": "Imminent Alerts", + "title_size": "16", + "title_align": "left", + "requests": [ + { + "formulas": [ + { + "formula": "query2", + "limit": { + "order": "desc" + } + } + ], + "response_format": "scalar", + "queries": [ + { + "search": { + "query": "source:my_apps message:\"Threat Level: imminent\"" + }, + "data_source": "events", + "compute": { + "aggregation": "count" + }, + "name": "query2", + "indexes": [ + "*" + ], + "group_by": [] + } + ] + } + ], + "type": "sunburst", + "legend": { + "type": "automatic" + } + }, + "layout": { + "x": 0, + "y": 8, + "width": 4, + "height": 4 + } + }, + { + "id": 1434403194670864, + "definition": { + "title": "Emerging Alerts", + "title_size": "16", + "title_align": "left", + "requests": [ + { + "formulas": [ + { + "formula": "query2", + "limit": { + "order": "desc" + } + } + ], + "response_format": "scalar", + "queries": [ + { + "search": { + "query": "source:my_apps message:\"Threat Level: emerging\"" + }, + "data_source": "events", + "compute": { + "aggregation": "count" + }, + "name": "query2", + "indexes": [ + "*" + ], + "group_by": [] + } + ] + } + ], + "type": "sunburst", + "legend": { + "type": "automatic" + } + }, + "layout": { + "x": 4, + "y": 8, + "width": 4, + "height": 4 + } + } + ], + "template_variables": [], + "layout_type": "ordered", + "is_read_only": false, + "notify_list": [], + "reflow_type": "fixed", + "id": "tz2-pyd-932" + } + \ No newline at end of file diff --git a/cybersixgill_actionable_alerts/assets/service_checks.json b/cybersixgill_actionable_alerts/assets/service_checks.json new file mode 100644 index 0000000000..1343b307dd --- /dev/null +++ b/cybersixgill_actionable_alerts/assets/service_checks.json @@ -0,0 +1,26 @@ + [ + { + "agent_version": "7.37.1", + "integration": "cybersixgill_actionable_alerts", + "groups": [], + "check": "cybersixgill_actionable_alert.can_connect", + "statuses": [ + "ok", + "critical" + ], + "name": "Can Connect", + "description": "Returns `OK` If Client Id and Client Secret are present in an Instance. Returns `CRITICAL` If Configuration Errors occur." + }, + { + "agent_version": "7.37.1", + "integration": "cybersixgill_actionable_alerts", + "groups": [], + "check": "cybersixgill.health", + "statuses": [ + "ok", + "critical" + ], + "name": "Health", + "description": "Returns `CRITICAL` If the Agent is unable to connect to Cybersixgill API" + } +] diff --git a/cybersixgill_actionable_alerts/datadog_checks/__init__.py b/cybersixgill_actionable_alerts/datadog_checks/__init__.py new file mode 100644 index 0000000000..0d1f7edf5d --- /dev/null +++ b/cybersixgill_actionable_alerts/datadog_checks/__init__.py @@ -0,0 +1 @@ +__path__ = __import__('pkgutil').extend_path(__path__, __name__) # type: ignore diff --git a/cybersixgill_actionable_alerts/datadog_checks/cybersixgill_actionable_alerts/__about__.py b/cybersixgill_actionable_alerts/datadog_checks/cybersixgill_actionable_alerts/__about__.py new file mode 100644 index 0000000000..b8023d8bc0 --- /dev/null +++ b/cybersixgill_actionable_alerts/datadog_checks/cybersixgill_actionable_alerts/__about__.py @@ -0,0 +1 @@ +__version__ = '0.0.1' diff --git a/cybersixgill_actionable_alerts/datadog_checks/cybersixgill_actionable_alerts/__init__.py b/cybersixgill_actionable_alerts/datadog_checks/cybersixgill_actionable_alerts/__init__.py new file mode 100644 index 0000000000..8b16295d4f --- /dev/null +++ b/cybersixgill_actionable_alerts/datadog_checks/cybersixgill_actionable_alerts/__init__.py @@ -0,0 +1,4 @@ +from .__about__ import __version__ +from .check import CybersixgillActionableAlertsCheck + +__all__ = ['__version__', 'CybersixgillActionableAlertsCheck'] diff --git a/cybersixgill_actionable_alerts/datadog_checks/cybersixgill_actionable_alerts/check.py b/cybersixgill_actionable_alerts/datadog_checks/cybersixgill_actionable_alerts/check.py new file mode 100644 index 0000000000..4a4bb8bce2 --- /dev/null +++ b/cybersixgill_actionable_alerts/datadog_checks/cybersixgill_actionable_alerts/check.py @@ -0,0 +1,147 @@ +import json +import os +from datetime import datetime, timedelta + +from requests.exceptions import ConnectionError, HTTPError, Timeout +from sixgill.sixgill_actionable_alert_client import SixgillActionableAlertClient + +from datadog_checks.base import AgentCheck, ConfigurationError + +Channel_Id = "d5cd46c205c20c87006b55a18b106428" + + +class CybersixgillActionableAlertsCheck(AgentCheck): + SERVICE_CHECK_CONNECT_NAME = 'cybersixgill.can_connect' + SERVICE_CHECK_HEALTH_NAME = 'cybersixgill.health' + + def __init__(self, name, init_config, instances): + super(CybersixgillActionableAlertsCheck, self).__init__(name, init_config, instances) + + def check(self, instance): + cl_id = self.instance.get('client_id') + cl_secret = self.instance.get('client_secret') + if not cl_id and cl_secret: + raise ConfigurationError + alerts_limit = self.instance.get('alerts_limit') + threat_level = self.instance.get('threat_level') + threat_type = self.instance.get('threat_type') + organization_id = self.instance.get('organization_id') + if not threat_level: + threat_level = None + threat_type_list = None + if not organization_id: + organization_id = None + if threat_type: + threat_type_list = threat_type.split(", ") + try: + file_dir = os.path.dirname(__file__) + abs_file_path = os.path.join(file_dir, "date_threshold.txt") + if os.path.exists(abs_file_path): + file_data = open(abs_file_path, 'r') + req_date = file_data.read() + date_conv = json.loads(req_date) + time_stamp_str = date_conv["from_date"] + if time_stamp_str is not None: + from_datetime = datetime.strptime(time_stamp_str, "%Y-%m-%d %H:%M:%S") + else: + from_datetime = datetime.now() + str_from_date = from_datetime.strftime("%Y-%m-%d %H:%M:%S") + str_from_date = datetime.strptime(str_from_date, "%Y-%m-%d %H:%M:%S") + from_datetime = str_from_date - timedelta(days=90) + else: + from_datetime = datetime.now() + str_from_date = from_datetime.strftime("%Y-%m-%d %H:%M:%S") + str_from_date = datetime.strptime(str_from_date, "%Y-%m-%d %H:%M:%S") + from_datetime = str_from_date - timedelta(days=90) + to_datetime = datetime.now() + str_today = to_datetime.strftime("%Y-%m-%d %H:%M:%S") + str_today = datetime.strptime(str_today, "%Y-%m-%d %H:%M:%S") + to_datetime = str_today + sixgill_client = SixgillActionableAlertClient( + client_id=cl_id, client_secret=cl_secret, channel_id=Channel_Id, verify=True + ) + alerts = sixgill_client.get_actionable_alerts_bulk( + limit=alerts_limit, + from_date=from_datetime, + to_date=to_datetime, + threat_type=threat_type_list, + threat_level=threat_level, + organization_id=organization_id, + ) + for al in alerts: + alert_id = al.get("id") + portal_url = f"https://portal.cybersixgill.com/#/?actionable_alert={alert_id}" + alert_info = sixgill_client.get_actionable_alert(alert_id) + additional_info = alert_info.get("additional_info") + event_dict = { + "event_type": al.get("threat_level"), + "msg_title": al.get("title"), + "aggregation_key": al.get("id"), + "source_type_name": "Cybersixgill", + "tags": al.get("threats", []), + "priority": "normal", + "msg_text": f"Cybersixgill Portal URL: {portal_url}\n" + f"Description: {alert_info.get('description')}\n" + f"Content Type: {alert_info.get('content_type')}\n" + f"Created time: {alert_info.get('create_time')}\n" + f"Attributes: {alert_info.get('additional_info', {}).get('asset_attributes')}\n" + f"Threat Level: {alert_info.get('threat_level', 'Unknown')}\n" + f"Threat Type: {alert_info.get('threats', [])}\n" + f"Matched Assets: {alert_info.get('matched_assets', {})}\n", + } + if al.get('content', []): + event_dict["msg_text"] += f"Content: {al.get('content', [])}\n" + if alert_info.get('assessment'): + event_dict["msg_text"] += f"Assessment: {alert_info.get('assessment', None)}\n" + if alert_info.get('recommendations', []): + event_dict["msg_text"] += f"Recommendations: {alert_info.get('recommendations', [])}\n" + if additional_info: + if 'cve_id' in additional_info: + event_dict["msg_text"] += f'Main CVE ID: {additional_info.get("cve_id")}\n' + event_dict["msg_text"] += f'CVE List: {additional_info.get("cve_list", [])}\n' + event_dict["msg_text"] += ( + f'CVE Link: "https://portal.cybersixgill.com/#/cve/' + f'{additional_info.get("cve_id", "")}"\n ' + ) + event_dict[ + "msg_text" + ] += f'CVSS 3.1: {additional_info.get("nvd", {}).get("v3", {}).get("current")}\n' + event_dict[ + "msg_text" + ] += f'CVSS 2.0: {additional_info.get("nvd", {}).get("v2", {}).get("current")}\n' + event_dict["msg_text"] += f'DVE Score: {additional_info.get("score", {}).get("current")}\n' + attributes = [] + attributes_dict = {} + for att in additional_info.get("attributes"): + if att.get("value"): + attributes_dict["Name"] = att.get("name") + attributes_dict["Description"] = att.get("description") + attributes.append(attributes_dict) + event_dict["msg_text"] += f'CVE Attributes: {attributes}' + if "sub_alerts" in al: + sub_alerts = al.get('sub_alerts', []) + for sub in sub_alerts: + event_dict["msg_text"] += ( + f"Aggregate Alert ID: {sub.get('aggregate_alert_id')}\n" + f"Content{sub.get('content')}\n" + f"Date{sub.get('date')}\n" + f"Read{sub.get('read')}\n" + f"Matched Assets: {sub.get('matched_assets')}\n" + f"{sub.get('site')}" + ) + self.event(event_dict) + last_alert_time = alert_info.get("create_time") + # self.write_persistent_cache("from_date", last_alert_time) + time_dict = {'from_date': last_alert_time} + txt_file_update = open(abs_file_path, 'w') + txt_file_update.write(json.dumps(time_dict)) + txt_file_update.close() + self.service_check(self.SERVICE_CHECK_CONNECT_NAME, AgentCheck.OK) + self.service_check(self.SERVICE_CHECK_HEALTH_NAME, AgentCheck.OK) + except (Timeout, HTTPError, ConnectionError) as e: + error_message = f"Request Timeout {e}" + self.service_check(self.SERVICE_CHECK_CONNECT_NAME, AgentCheck.CRITICAl, message=error_message) + raise ConfigurationError + except Exception as e: + self.service_check(self.SERVICE_CHECK_HEALTH_NAME, AgentCheck.CRITICAL, message=str(e)) + raise diff --git a/cybersixgill_actionable_alerts/datadog_checks/cybersixgill_actionable_alerts/config_models/__init__.py b/cybersixgill_actionable_alerts/datadog_checks/cybersixgill_actionable_alerts/config_models/__init__.py new file mode 100644 index 0000000000..5c2bf5c9f4 --- /dev/null +++ b/cybersixgill_actionable_alerts/datadog_checks/cybersixgill_actionable_alerts/config_models/__init__.py @@ -0,0 +1,20 @@ +# This file is autogenerated. +# To change this file you should edit assets/configuration/spec.yaml and then run the following commands: +# ddev -x validate config -s +# ddev -x validate models -s + +from .instance import InstanceConfig +from .shared import SharedConfig + + +class ConfigMixin: + _config_model_instance: InstanceConfig + _config_model_shared: SharedConfig + + @property + def config(self) -> InstanceConfig: + return self._config_model_instance + + @property + def shared_config(self) -> SharedConfig: + return self._config_model_shared diff --git a/cybersixgill_actionable_alerts/datadog_checks/cybersixgill_actionable_alerts/config_models/defaults.py b/cybersixgill_actionable_alerts/datadog_checks/cybersixgill_actionable_alerts/config_models/defaults.py new file mode 100644 index 0000000000..08df1dd689 --- /dev/null +++ b/cybersixgill_actionable_alerts/datadog_checks/cybersixgill_actionable_alerts/config_models/defaults.py @@ -0,0 +1,26 @@ +# This file is autogenerated. +# To change this file you should edit assets/configuration/spec.yaml and then run the following commands: +# ddev -x validate config -s +# ddev -x validate models -s + +from datadog_checks.base.utils.models.fields import get_default_field_value + + +def shared_service(field, value): + return get_default_field_value(field, value) + + +def instance_alerts_limit(field, value): + return get_default_field_value(field, value) + + +def instance_organization_id(field, value): + return get_default_field_value(field, value) + + +def instance_threat_level(field, value): + return get_default_field_value(field, value) + + +def instance_threat_type(field, value): + return get_default_field_value(field, value) diff --git a/cybersixgill_actionable_alerts/datadog_checks/cybersixgill_actionable_alerts/config_models/instance.py b/cybersixgill_actionable_alerts/datadog_checks/cybersixgill_actionable_alerts/config_models/instance.py new file mode 100644 index 0000000000..36885c97a6 --- /dev/null +++ b/cybersixgill_actionable_alerts/datadog_checks/cybersixgill_actionable_alerts/config_models/instance.py @@ -0,0 +1,64 @@ +# This file is autogenerated. +# To change this file you should edit assets/configuration/spec.yaml and then run the following commands: +# ddev -x validate config -s +# ddev -x validate models -s + +from __future__ import annotations + +from typing import Literal, Optional + +from pydantic import BaseModel, root_validator, validator + +from datadog_checks.base.utils.functions import identity +from datadog_checks.base.utils.models import validation + +from . import defaults, validators + + +class InstanceConfig(BaseModel): + class Config: + allow_mutation = False + + alerts_limit: Optional[int] + cl_id: str + cl_secret: str + organization_id: Optional[str] + threat_level: Optional[str] + threat_type: Optional[ + Literal[ + 'Brand Protection', + 'Data Leak', + 'Malware', + 'Phishing', + 'Fraud', + 'Vulnerability Exploit', + 'Insider Threat', + 'Defacement', + 'Compromised Accounts', + 'DDoS Attack', + 'Web Attack', + 'Trend Anomaly', + ] + ] + + @root_validator(pre=True) + def _initial_validation(cls, values): + return validation.core.initialize_config(getattr(validators, 'initialize_instance', identity)(values)) + + @validator('*', pre=True, always=True) + def _ensure_defaults(cls, v, field): + if v is not None or field.required: + return v + + return getattr(defaults, f'instance_{field.name}')(field, v) + + @validator('*') + def _run_validations(cls, v, field): + if not v: + return v + + return getattr(validators, f'instance_{field.name}', identity)(v, field=field) + + @root_validator(pre=False) + def _final_validation(cls, values): + return validation.core.finalize_config(getattr(validators, 'finalize_instance', identity)(values)) diff --git a/cybersixgill_actionable_alerts/datadog_checks/cybersixgill_actionable_alerts/config_models/shared.py b/cybersixgill_actionable_alerts/datadog_checks/cybersixgill_actionable_alerts/config_models/shared.py new file mode 100644 index 0000000000..16f6c25ddd --- /dev/null +++ b/cybersixgill_actionable_alerts/datadog_checks/cybersixgill_actionable_alerts/config_models/shared.py @@ -0,0 +1,44 @@ +# This file is autogenerated. +# To change this file you should edit assets/configuration/spec.yaml and then run the following commands: +# ddev -x validate config -s +# ddev -x validate models -s + +from __future__ import annotations + +from typing import Optional + +from pydantic import BaseModel, root_validator, validator + +from datadog_checks.base.utils.functions import identity +from datadog_checks.base.utils.models import validation + +from . import defaults, validators + + +class SharedConfig(BaseModel): + class Config: + allow_mutation = False + + service: Optional[str] + + @root_validator(pre=True) + def _initial_validation(cls, values): + return validation.core.initialize_config(getattr(validators, 'initialize_shared', identity)(values)) + + @validator('*', pre=True, always=True) + def _ensure_defaults(cls, v, field): + if v is not None or field.required: + return v + + return getattr(defaults, f'shared_{field.name}')(field, v) + + @validator('*') + def _run_validations(cls, v, field): + if not v: + return v + + return getattr(validators, f'shared_{field.name}', identity)(v, field=field) + + @root_validator(pre=False) + def _final_validation(cls, values): + return validation.core.finalize_config(getattr(validators, 'finalize_shared', identity)(values)) diff --git a/cybersixgill_actionable_alerts/datadog_checks/cybersixgill_actionable_alerts/config_models/validators.py b/cybersixgill_actionable_alerts/datadog_checks/cybersixgill_actionable_alerts/config_models/validators.py new file mode 100644 index 0000000000..39523e4f92 --- /dev/null +++ b/cybersixgill_actionable_alerts/datadog_checks/cybersixgill_actionable_alerts/config_models/validators.py @@ -0,0 +1,9 @@ +# Here you can include additional config validators or transformers +# +# def initialize_instance(values, **kwargs): +# if 'my_option' not in values and 'my_legacy_option' in values: +# values['my_option'] = values['my_legacy_option'] +# if values.get('my_number') > 10: +# raise ValueError('my_number max value is 10, got %s' % str(values.get('my_number'))) +# +# return values diff --git a/cybersixgill_actionable_alerts/datadog_checks/cybersixgill_actionable_alerts/data/conf.yaml.example b/cybersixgill_actionable_alerts/datadog_checks/cybersixgill_actionable_alerts/data/conf.yaml.example new file mode 100644 index 0000000000..e3d3dcd08d --- /dev/null +++ b/cybersixgill_actionable_alerts/datadog_checks/cybersixgill_actionable_alerts/data/conf.yaml.example @@ -0,0 +1,84 @@ +## All options defined here are available to all instances. +# +init_config: + + ## @param service - string - optional + ## Attach the tag `service:` to every metric, event, and service check emitted by this integration. + ## + ## Additionally, this sets the default `service` for every log source. + # + # service: + +## Every instance is scheduled independently of the others. +# +instances: + + ## @param cl_id - string - required + ## The Client Id given by Cybersixgill + # + - cl_id: clientid + + ## @param cl_secret - string - required + ## The Client Secret given by Cybersixgill + # + cl_secret: + + ## @param alerts_limit - integer - optional + ## The number of alerts to fetch on a single request default is 50 + # + # alerts_limit: 50 + + ## @param threat_type - string - optional + ## Predefined types of threats alerts you would like to see like fraud, malware + # + # threat_type: compromised accounts, fraud + + ## @param threat_level - string - optional + ## Type of alerts which are either imminent or emerging + # + # threat_level: imminent + + ## @param organization_id - string - optional + ## The Organization Id provided by Cybersixgill + # + # organization_id: orgidexample + +## @param tags - list of strings - optional +## A list of tags to attach to every metric and service check emitted by this instance. +## +## Learn more about tagging at https://docs.datadoghq.com/tagging +# +# tags: +# - : +# - : + +## @param service - string - optional +## Attach the tag `service:` to every metric, event, and service check emitted by this integration. +## +## Overrides any `service` defined in the `init_config` section. +# +# service: + +## @param min_collection_interval - number - optional - default: 15 +## This changes the collection interval of the check. For more information, see: +## https://docs.datadoghq.com/developers/write_agent_check/#collection-interval +# +# min_collection_interval: 15 + +## @param empty_default_hostname - boolean - optional - default: false +## This forces the check to send metrics with no hostname. +## +## This is useful for cluster-level checks. +# +# empty_default_hostname: false + +## @param metric_patterns - mapping - optional +## A mapping of metrics to include or exclude, with each entry being a regular expression. +## +## Metrics defined in `exclude` will take precedence in case of overlap. +# +# metric_patterns: +# include: +# - +# exclude: +# - diff --git a/cybersixgill_actionable_alerts/datadog_checks/cybersixgill_actionable_alerts/date_threshold.txt b/cybersixgill_actionable_alerts/datadog_checks/cybersixgill_actionable_alerts/date_threshold.txt new file mode 100644 index 0000000000..55ca945b27 --- /dev/null +++ b/cybersixgill_actionable_alerts/datadog_checks/cybersixgill_actionable_alerts/date_threshold.txt @@ -0,0 +1 @@ +{"from_date": null} \ No newline at end of file diff --git a/cybersixgill_actionable_alerts/hatch.toml b/cybersixgill_actionable_alerts/hatch.toml new file mode 100644 index 0000000000..722d980697 --- /dev/null +++ b/cybersixgill_actionable_alerts/hatch.toml @@ -0,0 +1,7 @@ +[env.collectors.datadog-checks] + +[[envs.default.matrix]] +python = ["38"] + +[envs.default] +e2e-env = false \ No newline at end of file diff --git a/cybersixgill_actionable_alerts/images/cybersixgill_logo_svg.svg b/cybersixgill_actionable_alerts/images/cybersixgill_logo_svg.svg new file mode 100644 index 0000000000..6208139402 --- /dev/null +++ b/cybersixgill_actionable_alerts/images/cybersixgill_logo_svg.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cybersixgill_actionable_alerts/images/dashboard_count.PNG b/cybersixgill_actionable_alerts/images/dashboard_count.PNG new file mode 100644 index 0000000000..35109eabaa Binary files /dev/null and b/cybersixgill_actionable_alerts/images/dashboard_count.PNG differ diff --git a/cybersixgill_actionable_alerts/images/dashboard_emerging_alerts_count.PNG b/cybersixgill_actionable_alerts/images/dashboard_emerging_alerts_count.PNG new file mode 100644 index 0000000000..dfcfb00fc3 Binary files /dev/null and b/cybersixgill_actionable_alerts/images/dashboard_emerging_alerts_count.PNG differ diff --git a/cybersixgill_actionable_alerts/images/dashboard_events_list.PNG b/cybersixgill_actionable_alerts/images/dashboard_events_list.PNG new file mode 100644 index 0000000000..831436c3c5 Binary files /dev/null and b/cybersixgill_actionable_alerts/images/dashboard_events_list.PNG differ diff --git a/cybersixgill_actionable_alerts/images/dashboard_imminent_alerts_count.PNG b/cybersixgill_actionable_alerts/images/dashboard_imminent_alerts_count.PNG new file mode 100644 index 0000000000..1061d571a8 Binary files /dev/null and b/cybersixgill_actionable_alerts/images/dashboard_imminent_alerts_count.PNG differ diff --git a/cybersixgill_actionable_alerts/images/transparent.png b/cybersixgill_actionable_alerts/images/transparent.png new file mode 100644 index 0000000000..2d5657a15e Binary files /dev/null and b/cybersixgill_actionable_alerts/images/transparent.png differ diff --git a/cybersixgill_actionable_alerts/manifest.json b/cybersixgill_actionable_alerts/manifest.json new file mode 100644 index 0000000000..e572dd5843 --- /dev/null +++ b/cybersixgill_actionable_alerts/manifest.json @@ -0,0 +1,75 @@ +{ + "manifest_version": "2.0.0", + "app_uuid": "b27feb80-b06f-4200-981a-e91a031d62e6", + "app_id": "cybersixgill-actionable-alerts", + "display_on_public_website": true, + "tile": { + "overview": "README.md#Overview", + "configuration": "README.md#Setup", + "support": "README.md#Support", + "changelog": "CHANGELOG.md", + "description": "Monitor the activity of assets and provide real-time alerts on incoming threats", + "title": "Cybersixgill Actionable Alerts", + "media": [ + { + "media_type": "image", + "caption": "Dashboard image of alerts count", + "image_url": "images/dashboard_count.PNG" + }, + { + "media_type": "image", + "caption": "Dashboard image of events list with title", + "image_url": "images/dashboard_emerging_alerts_count.PNG" + }, + { + "media_type": "image", + "caption": "Dashboard image of emerging alerts count", + "image_url": "images/dashboard_events_list.PNG" + }, + { + "media_type": "image", + "caption": "Dashboard image of imminent alerts count", + "image_url": "images/dashboard_imminent_alerts_count.PNG" + } + ], + "classifier_tags": [ + "Offering::Integration", + "Supported OS::Linux", + "Supported OS::Windows", + "Supported OS::macOS", + "Category::Security", + "Category::Monitoring", + "Category::Event Management", + "Submitted Data Type::Events" + ] + }, + "assets": { + "integration": { + "source_type_name": "cybersixgill_actionable_alerts", + "configuration": { + "spec": "assets/configuration/spec.yaml" + }, + "events": { + "creates_events": true + }, + "metrics": { + "prefix": "cybersixgill_actionable_alerts.", + "check": [], + "metadata_path": "metadata.csv" + }, + "service_checks": { + "metadata_path": "assets/service_checks.json" + } + }, + "dashboards": { + "cybersixgill": "assets/dashboards/cybersixgill_actionable_alerts_overview.json" + } + }, + "author": { + "support_email": "support@cyebrsixgill.com", + "name": "Cybersixgill", + "homepage": "https://www.cybersixgill.com/", + "sales_email": "info@cybersixgill.com" + }, + "oauth": {} +} diff --git a/cybersixgill_actionable_alerts/metadata.csv b/cybersixgill_actionable_alerts/metadata.csv new file mode 100644 index 0000000000..ae0af07419 --- /dev/null +++ b/cybersixgill_actionable_alerts/metadata.csv @@ -0,0 +1 @@ +metric_name,metric_type,interval,unit_name,per_unit_name,description,orientation,integration,short_name diff --git a/cybersixgill_actionable_alerts/pyproject.toml b/cybersixgill_actionable_alerts/pyproject.toml new file mode 100644 index 0000000000..96aa12c87b --- /dev/null +++ b/cybersixgill_actionable_alerts/pyproject.toml @@ -0,0 +1,62 @@ +[build-system] +requires = [ + "hatchling>=0.13.0", + "setuptools<61", +] +build-backend = "hatchling.build" + +[project] +name = "datadog-cybersixgill-actionable-alerts" +description = "The cybersixgill_actionable_alerts check" +readme = "README.md" +license = {text = "BSD-3-Clause"} +requires-python = ">=3.8" +keywords = [ + "datadog", + "datadog agent", + "datadog check", + "cybersixgill_actionable_alerts", +] +authors = [ + { name = "Farukh Shaik", email = "fshaik@loginsoft.com" }, +] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Intended Audience :: System Administrators", + "License :: OSI Approved :: BSD License", + "Private :: Do Not Upload", + "Programming Language :: Python :: 3.8", + "Topic :: System :: Monitoring", +] +dependencies = [ + "datadog-checks-base>=25.1.0", + "sixgill-clients>=0.2.24", +] +dynamic = [ + "version", +] + +[project.optional-dependencies] +deps = [] + +[project.urls] +Source = "https://github.com/DataDog/integrations-extras" + +[tool.hatch.version] +path = "datadog_checks/cybersixgill_actionable_alerts/__about__.py" + +[tool.hatch.build.targets.sdist] +include = [ + "/datadog_checks", + "/tests", + "/manifest.json", +] + +[tool.hatch.build.targets.wheel] +include = [ + "/datadog_checks/cybersixgill_actionable_alerts", +] +dev-mode-dirs = [ + ".", +] diff --git a/cybersixgill_actionable_alerts/requirements.txt b/cybersixgill_actionable_alerts/requirements.txt new file mode 100644 index 0000000000..4cf8cd2bfa --- /dev/null +++ b/cybersixgill_actionable_alerts/requirements.txt @@ -0,0 +1,5 @@ +pytest~=7.1.3 +setuptools~=57.0.0 +pydantic~=1.9.2 +sixgill-clients~=0.2.24 +requests~=2.28.1 \ No newline at end of file diff --git a/cybersixgill_actionable_alerts/setup.py b/cybersixgill_actionable_alerts/setup.py new file mode 100644 index 0000000000..dd6b3cc5e1 --- /dev/null +++ b/cybersixgill_actionable_alerts/setup.py @@ -0,0 +1,76 @@ +from codecs import open # To use a consistent encoding +from os import path + +from setuptools import setup + +HERE = path.dirname(path.abspath(__file__)) + +# Get version info +ABOUT = {} +with open(path.join(HERE, 'datadog_checks', 'cybersixgill_actionable_alerts', '__about__.py')) as f: + exec(f.read(), ABOUT) + +# Get the long description from the README file +with open(path.join(HERE, 'README.md'), encoding='utf-8') as f: + long_description = f.read() + + +def get_dependencies(): + dep_file = path.join(HERE, 'requirements.in') + if not path.isfile(dep_file): + return [] + + with open(dep_file, encoding='utf-8') as f: + return f.readlines() + + +def parse_pyproject_array(name): + import os + import re + from ast import literal_eval + + pattern = r'^{} = (\[.*?\])$'.format(name) + + with open(os.path.join(HERE, 'pyproject.toml'), 'r', encoding='utf-8') as f: + # Windows \r\n prevents match + contents = '\n'.join(line.rstrip() for line in f.readlines()) + + array = re.search(pattern, contents, flags=re.MULTILINE | re.DOTALL).group(1) + return literal_eval(array) + + +CHECKS_BASE_REQ = parse_pyproject_array('dependencies')[0] + + +setup( + name='datadog-cybersixgill_actionable_alerts', + version=ABOUT['__version__'], + description='The cybersixgill_actionable_alerts check', + long_description=long_description, + long_description_content_type='text/markdown', + keywords='datadog agent cybersixgill_actionable_alerts check', + # The project's main homepage. + url='https://github.com/DataDog/integrations-extras', + # Author details + author='Farukh Shaik', + author_email='fshaik@loginsoft.com', + # License + license='BSD-3-Clause', + # See https://pypi.org/classifiers + classifiers=[ + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Developers', + 'Intended Audience :: System Administrators', + 'Topic :: System :: Monitoring', + 'License :: OSI Approved :: BSD License', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.8', + ], + # The package we're going to ship + packages=['datadog_checks.cybersixgill_actionable_alerts'], + # Run-time dependencies + install_requires=[CHECKS_BASE_REQ], + extras_require={'deps': parse_pyproject_array('deps')}, + # Extra files to ship with the wheel package + include_package_data=True, +) diff --git a/cybersixgill_actionable_alerts/tests/__init__.py b/cybersixgill_actionable_alerts/tests/__init__.py new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/cybersixgill_actionable_alerts/tests/__init__.py @@ -0,0 +1 @@ + diff --git a/cybersixgill_actionable_alerts/tests/conftest.py b/cybersixgill_actionable_alerts/tests/conftest.py new file mode 100644 index 0000000000..00f3ccbe5a --- /dev/null +++ b/cybersixgill_actionable_alerts/tests/conftest.py @@ -0,0 +1,11 @@ +import pytest + + +@pytest.fixture(scope='session') +def dd_environment(): + yield + + +@pytest.fixture +def instance(): + return {} diff --git a/cybersixgill_actionable_alerts/tests/test_cybersixgill_actionable_alerts.py b/cybersixgill_actionable_alerts/tests/test_cybersixgill_actionable_alerts.py new file mode 100644 index 0000000000..858d2bb545 --- /dev/null +++ b/cybersixgill_actionable_alerts/tests/test_cybersixgill_actionable_alerts.py @@ -0,0 +1,184 @@ +import json +import os +from datetime import datetime + +import pytest + +from datadog_checks.cybersixgill_actionable_alerts.check import ( + CybersixgillActionableAlertsCheck, +) + +# from ..datadog_checks.cybersixgill_actionable_alerts.check import CybersixgillActionableAlertsCheck + +incidents_list = [ + { + "alert_name": "Your organization was potentially targeted by a ransomware group", + "content": "text", + "date": "2021-11-08 06:01:05", + "id": "6188bd21017198385e228437", + "read": True, + "severity": 1, + "site": "rw_everest", + "status": {"name": "in_treatment", "user": "60b604a048ce2cb294629a2d"}, + "threat_level": "imminent", + "threats": ["Brand Protection", "Data Leak"], + "title": "Your organization was potentially targeted by a ransomware group", + "user_id": "5d233575f8db38787dbe24b6", + }, + { + "alert_name": "Gift Cards of {organization_name} are Sold on the Underground ", + "category": "regular", + "content": "text", + "date": "2021-11-02 06:00:27", + "id": "6180d4011dbb8edcb496ec8b", + "lang": "English", + "langcode": "en", + "read": False, + "threat_level": "imminent", + "threats": ["Fraud"], + "title": "Gift Cards of Sixgill are Sold on the Underground ", + "user_id": "5d233575f8db38787dbe24b6", + }, +] + +info_item = { + "alert_id": "616ffed97a1b66036a138f73", + "alert_name": "Your organization was potentially targeted by a ransomware group", + "alert_type": "QueryBasedManagedAlertRule", + "assessment": "text", + "category": "regular", + "content_type": "search_result_item", + "description": 'A ransomware group posted on its leak site, rw_everest, focusing on "Walmart" ', +} + +expected_alert_output = { + "event_type": "imminent", + "api_key": "c16896127b23db13ff905496e339a0bf", + "msg_title": "Name of the alert", + "aggregation_key": "324234234122", + "source_type_name": "Cybersixgill", + "tags": ["fraud", "malware"], + "priority": "normal", + "msg_txt": "Cybersixgill Portal URL- https://portal.cybersixgill.com/#/?actionable_alert=634\n" + "Description- Sixgill discovered 182 Cybersixgill compromised credit cards\n" + "Content Type- table_content_item\n" + "Created time- 2022-10-11 05:04:52\n" + "Attributes- None\n" + "Threat Level- imminent\n" + "Threat Type- ['Fraud', 'Compromised Accounts']\n" + "Matched Assets- ['404137', '401795']", +} + + +def test_config_empty(aggregator): + instance = {} + with pytest.raises(Exception): + c = CybersixgillActionableAlertsCheck('cybersixgill_actionable_alerts', {}, [instance]) + c.check(instance) + aggregator.assert_service_check( + CybersixgillActionableAlertsCheck.SERVICE_CHECK_HEALTH_NAME, + CybersixgillActionableAlertsCheck.CRITICAL, + ) + + +def test_invalid_config(aggregator): + instance = {"cl_id": "clientid", "cl_secret": "clientsecret"} + with pytest.raises(Exception): + c = CybersixgillActionableAlertsCheck('cybersixgill_actionable_alerts', {}, [instance]) + c.check(instance) + aggregator.assert_service_check( + CybersixgillActionableAlertsCheck.SERVICE_CHECK_HEALTH_NAME, + CybersixgillActionableAlertsCheck.CRITICAL, + ) + + +def test_file_data_with_null_date(aggregator): + from unittest import mock + + file_contents = json.dumps({'from_date': ""}) + instance = {"cl_id": "clientid", "cl_secret": "clientsecret"} + with pytest.raises(Exception): + with mock.patch('builtins.open', mock.mock_open(read_data=file_contents)): + c = CybersixgillActionableAlertsCheck('cybersixgill_actionable_alerts', {}, [instance]) + c.check(instance) + aggregator.assert_service_check( + CybersixgillActionableAlertsCheck.SERVICE_CHECK_HEALTH_NAME, + CybersixgillActionableAlertsCheck.CRITICAL, + ) + + +def test_file_data(aggregator): + from unittest import mock + + file_contents = json.dumps({'from_date': datetime.now().strftime("%Y-%m-%d %H:%M:%S")}) + instance = {"cl_id": "clientid", "cl_secret": "clientsecret"} + with pytest.raises(Exception): + with mock.patch('builtins.open', mock.mock_open(read_data=file_contents)): + c = CybersixgillActionableAlertsCheck('cybersixgill_actionable_alerts', {}, [instance]) + c.check(instance) + aggregator.assert_service_check( + CybersixgillActionableAlertsCheck.SERVICE_CHECK_HEALTH_NAME, + CybersixgillActionableAlertsCheck.CRITICAL, + ) + + +def test_set_file_path_null(aggregator, monkeypatch): + instance = {"cl_id": "clientid", "cl_secret": "clientsecret"} + with pytest.raises(Exception): + monkeypatch.setattr(os.path, 'dirname', lambda path: '') + c = CybersixgillActionableAlertsCheck('cybersixgill_actionable_alerts', {}, [instance]) + c.check(instance) + aggregator.assert_service_check( + CybersixgillActionableAlertsCheck.SERVICE_CHECK_HEALTH_NAME, + CybersixgillActionableAlertsCheck.CRITICAL, + ) + + +# def test_file_date(aggregator): +# file_dir = os.path.dirname(__file__) +# abs_file_path = os.path.join(file_dir, "date_threshold.txt") + +# if os.path.exists(abs_file_path): +# with open(abs_file_path, "r") as file_data: +# json.loads(file_data.read()) +# aggregator.assert_service_check( +# CybersixgillActionableAlertsCheck.SERVICE_CHECK_HEALTH_NAME, +# CybersixgillActionableAlertsCheck.CRITICAL, +# ) +# # time_stamp_str = date_conv["from_date"] +# # if time_stamp_str is not None: +# # from_datetime = datetime.strptime(time_stamp_str, "%Y-%m-%d %H:%M:%S") +# # else: +# # from_datetime = datetime.now() +# # str_from_date = from_datetime.strftime("%Y-%m-%d %H:%M:%S") +# # str_from_date = datetime.strptime(str_from_date, "%Y-%m-%d %H:%M:%S") +# # from_datetime = str_from_date - timedelta(days=90) +# # assert isinstance(from_datetime, datetime) +# # assert from_datetime < datetime.now() + + +# def test_empty_file_path(): +# abs_file_path = "" +# if not os.path.exists(abs_file_path): +# from_datetime = datetime.now() +# str_from_date = from_datetime.strftime("%Y-%m-%d %H:%M:%S") +# str_from_date = datetime.strptime(str_from_date, "%Y-%m-%d %H:%M:%S") +# from_datetime = str_from_date - timedelta(days=90) +# assert isinstance(from_datetime, datetime) +# assert from_datetime < datetime.now() + + +def test_check(aggregator, instance, mocker): + from sixgill.sixgill_actionable_alert_client import SixgillActionableAlertClient + + c = CybersixgillActionableAlertsCheck('cybersixgill_actionable_alerts', {}, [instance]) + instance["cl_id"] = "clientid" + instance["cl_secret"] = "clientsecret" + mocker.patch.object(SixgillActionableAlertClient, 'get_actionable_alerts_bulk', return_value=incidents_list) + mocker.patch.object(SixgillActionableAlertClient, 'get_actionable_alert', return_value=info_item) + c.check(instance) + aggregator.assert_service_check( + CybersixgillActionableAlertsCheck.SERVICE_CHECK_CONNECT_NAME, + CybersixgillActionableAlertsCheck.OK, + ) + aggregator.assert_all_metrics_covered() diff --git a/cybersixgill_actionable_alerts/tests/test_instance.py b/cybersixgill_actionable_alerts/tests/test_instance.py new file mode 100644 index 0000000000..97bfcb219c --- /dev/null +++ b/cybersixgill_actionable_alerts/tests/test_instance.py @@ -0,0 +1,47 @@ +# from datadog_checks.cybersixgill_actionable_alerts.config_models.instance import InstanceConfig +from datadog_checks.cybersixgill_actionable_alerts.config_models.instance import ( + InstanceConfig, +) + + +def test_instance_config_initialization(): + # Test that an instance of InstanceConfig can be initialized with valid values + config = InstanceConfig( + alerts_limit=10, + cl_id="my_client_id", + cl_secret="my_client_secret", + organization_id="my_org_id", + threat_level="high", + threat_type="Malware", + ) + assert config.alerts_limit == 10 + assert config.cl_id == "my_client_id" + assert config.cl_secret == "my_client_secret" + assert config.organization_id == "my_org_id" + assert config.threat_level == "high" + assert config.threat_type == "Malware" + + +# +# def test_instance_config_missing_required_field(): +# # Test that an instance of InstanceConfig cannot be initialized without a required field +# with pytest.raises(ValueError): +# config = InstanceConfig( +# alerts_limit=10, +# cl_id='', +# cl_secret='my_client_secret', +# threat_level='high', +# threat_type='Malware' +# ) +# +# def test_instance_config_invalid_field_value(): +# # Test that an instance of InstanceConfig cannot be initialized with an invalid field value +# with pytest.raises(ValueError): +# config = InstanceConfig( +# alerts_limit=10, +# cl_id='my_client_id', +# cl_secret='my_client_secret', +# organization_id='my_org_id', +# threat_level='not_a_valid_threat_level', +# threat_type='Malware' +# ) diff --git a/cybersixgill_actionable_alerts/tests/test_shared.py b/cybersixgill_actionable_alerts/tests/test_shared.py new file mode 100644 index 0000000000..0b1425298e --- /dev/null +++ b/cybersixgill_actionable_alerts/tests/test_shared.py @@ -0,0 +1,31 @@ +# import pytest +# from datadog_checks.cybersixgill_actionable_alerts.config_models.shared import SharedConfig +from datadog_checks.cybersixgill_actionable_alerts.config_models.shared import ( + SharedConfig, +) + + +def test_shared_config_service(): + # Test that the service field is validated + shared_config = SharedConfig(service="my_service") + assert shared_config.service == "my_service" + + # # Test that the service field is not validated if it is None + # shared_config = SharedConfig(service=None) + # assert shared_config.service is '' + + +# def test_shared_config_defaults(): +# # Test that the default values are applied +# shared_config = SharedConfig() +# assert shared_config.field1 == "default_value1" +# assert shared_config.field2 == "default_value2" +# +# def test_shared_config_validations(): +# # Test that the validations are applied +# shared_config = SharedConfig(service="my_service", field1="invalid_value") +# with pytest.raises(ValueError): +# shared_config.validate() +# +# shared_config = SharedConfig(service="my_service", field1="valid_value") +# shared_config.validate()