diff --git a/Packs/Base/ReleaseNotes/1_35_0.md b/Packs/Base/ReleaseNotes/1_35_0.md new file mode 100644 index 000000000000..d30d8ed16a2d --- /dev/null +++ b/Packs/Base/ReleaseNotes/1_35_0.md @@ -0,0 +1,18 @@ + +#### Scripts + +##### CommonServerPython + +Added new fields to `Common.File`, `Common.URL`, `Common.Domain`, `Common.IP` indicator classes: + +- `organization_prevalence` + +- `globally_prevalence` + +- `organization_first_seen` + +- `organization_last_seen` + +- `first_seen_by_source` + +- `last_seen_by_source` diff --git a/Packs/Base/Scripts/CommonServerPython/CommonServerPython.py b/Packs/Base/Scripts/CommonServerPython/CommonServerPython.py index 4ba4040df2a5..f8b4e1f3ef31 100644 --- a/Packs/Base/Scripts/CommonServerPython/CommonServerPython.py +++ b/Packs/Base/Scripts/CommonServerPython/CommonServerPython.py @@ -3206,6 +3206,24 @@ class IP(Indicator): :type dbot_score: ``DBotScore`` :param dbot_score: If IP has a score then create and set a DBotScore object. + :type organization_prevalence: ``int`` + :param organization_prevalence: The number of times the indicator is detected in the organization. + + :type global_prevalence: ``int`` + :param global_prevalence: The number of times the indicator is detected across all organizations. + + :type organization_first_seen: ``str`` + :param organization_first_seen: ISO 8601 date time string; when the indicator was first seen in the organization. + + :type organization_last_seen: ``str`` + :param organization_last_seen: ISO 8601 date time string; the last time a specific organization encountered an indicator. + + :type first_seen_by_source: ``str`` + :param first_seen_by_source: ISO 8601 date time string; when the indicator was first seen by the source vendor. + + :type last_seen_by_source: ``str`` + :param last_seen_by_source: ISO 8601 date time string; when the indicator was last seen by the source vendor. + :return: None :rtype: ``None`` """ @@ -3221,7 +3239,9 @@ def __init__(self, ip, dbot_score, asn=None, as_owner=None, region=None, port=No geo_country=None, geo_description=None, detection_engines=None, positive_engines=None, organization_name=None, organization_type=None, feed_related_indicators=None, tags=None, malware_family=None, relationships=None, blocked=None, description=None, stix_id=None, - whois_records=None): + whois_records=None, organization_prevalence=None, + global_prevalence=None, organization_first_seen=None, organization_last_seen=None, + first_seen_by_source=None, last_seen_by_source=None): # Main value of the indicator self.ip = ip @@ -3263,6 +3283,12 @@ def __init__(self, ip, dbot_score, asn=None, as_owner=None, region=None, port=No self.feed_related_indicators = feed_related_indicators self.malware_family = malware_family self.relationships = relationships + self.organization_prevalence = organization_prevalence + self.global_prevalence = global_prevalence + self.organization_first_seen = organization_first_seen + self.organization_last_seen = organization_last_seen + self.first_seen_by_source = first_seen_by_source + self.last_seen_by_source = last_seen_by_source if not isinstance(dbot_score, Common.DBotScore): raise ValueError('dbot_score must be of type DBotScore') @@ -3374,6 +3400,24 @@ def to_context(self): if self.malware_family: ip_context['MalwareFamily'] = self.malware_family + if self.organization_prevalence is not None: # checking for `is not None` to allow `0`-value + ip_context['OrganizationPrevalence'] = self.organization_prevalence + + if self.global_prevalence is not None: # checking for `is not None` to allow `0`-value + ip_context['GlobalPrevalence'] = self.global_prevalence + + if self.organization_first_seen: + ip_context['OrganizationFirstSeen'] = self.organization_first_seen + + if self.organization_last_seen: + ip_context['OrganizationLastSeen'] = self.organization_last_seen + + if self.first_seen_by_source: + ip_context['FirstSeenBySource'] = self.first_seen_by_source + + if self.last_seen_by_source: + ip_context['LastSeenBySource'] = self.last_seen_by_source + if self.dbot_score and self.dbot_score.score == Common.DBotScore.BAD: ip_context['Malicious'] = { 'Vendor': self.dbot_score.integration_name, @@ -3872,6 +3916,24 @@ class File(Indicator): :type stix_id: ``str`` :param stix_id: File assigned STIX ID. + :type organization_prevalence: ``int`` + :param organization_prevalence: The number of times the indicator is detected in the organization. + + :type global_prevalence: ``int`` + :param global_prevalence: The number of times the indicator is detected across all organizations. + + :type organization_first_seen: ``str`` + :param organization_first_seen: ISO 8601 date time string; when the indicator was first seen in the organization. + + :type organization_last_seen: ``str`` + :param organization_last_seen: ISO 8601 date time string; the last time a specific organization encountered an indicator. + + :type first_seen_by_source: ``str`` + :param first_seen_by_source: ISO 8601 date time string; when the indicator was first seen by the source vendor. + + :type last_seen_by_source: ``str`` + :param last_seen_by_source: ISO 8601 date time string; when the indicator was last seen by the source vendor. + :rtype: ``None`` :return: None """ @@ -3886,7 +3948,9 @@ def __init__(self, dbot_score, name=None, entry_id=None, size=None, md5=None, sh feed_related_indicators=None, malware_family=None, imphash=None, quarantined=None, campaign=None, associated_file_names=None, traffic_light_protocol=None, organization=None, community_notes=None, publications=None, threat_types=None, behaviors=None, relationships=None, - creation_date=None, description=None, hashes=None, stix_id=None): + creation_date=None, description=None, hashes=None, stix_id=None, organization_prevalence=None, + global_prevalence=None, organization_first_seen=None, organization_last_seen=None, + first_seen_by_source=None, last_seen_by_source=None): # Main value of a file (Hashes) self.md5 = md5 @@ -3927,6 +3991,12 @@ def __init__(self, dbot_score, name=None, entry_id=None, size=None, md5=None, sh self.publications = publications self.threat_types = threat_types self.behaviors = behaviors + self.organization_prevalence = organization_prevalence + self.global_prevalence = global_prevalence + self.organization_first_seen = organization_first_seen + self.organization_last_seen = organization_last_seen + self.first_seen_by_source = first_seen_by_source + self.last_seen_by_source = last_seen_by_source # XSOAR Fields self.relationships = relationships @@ -4042,6 +4112,24 @@ def to_context(self): if self.behaviors: file_context['Behavior'] = self.create_context_table(self.behaviors) + if self.organization_prevalence is not None: # checking for `is not None` to allow `0`-value + file_context['OrganizationPrevalence'] = self.organization_prevalence + + if self.global_prevalence is not None: # checking for `is not None` to allow `0`-value + file_context['GlobalPrevalence'] = self.global_prevalence + + if self.organization_first_seen: + file_context['OrganizationFirstSeen'] = self.organization_first_seen + + if self.organization_last_seen: + file_context['OrganizationLastSeen'] = self.organization_last_seen + + if self.first_seen_by_source: + file_context['FirstSeenBySource'] = self.first_seen_by_source + + if self.last_seen_by_source: + file_context['LastSeenBySource'] = self.last_seen_by_source + if self.dbot_score and self.dbot_score.score == Common.DBotScore.BAD: file_context['Malicious'] = { 'Vendor': self.dbot_score.integration_name, @@ -4394,6 +4482,24 @@ class URL(Indicator): :type stix_id: ``str`` :param stix_id: The URL STIX ID. + :type organization_prevalence: ``int`` + :param organization_prevalence: The number of times the indicator is detected in the organization. + + :type global_prevalence: ``int`` + :param global_prevalence: The number of times the indicator is detected across all organizations. + + :type organization_first_seen: ``str`` + :param organization_first_seen: ISO 8601 date time string; when the indicator was first seen in the organization. + + :type organization_last_seen: ``str`` + :param organization_last_seen: ISO 8601 date time string; the last time a specific organization encountered an indicator. + + :type first_seen_by_source: ``str`` + :param first_seen_by_source: ISO 8601 date time string; when the indicator was first seen by the source vendor. + + :type last_seen_by_source: ``str`` + :param last_seen_by_source: ISO 8601 date time string; when the indicator was last seen by the source vendor. + :return: None :rtype: ``None`` """ @@ -4403,7 +4509,9 @@ def __init__(self, url, dbot_score, detection_engines=None, positive_detections= feed_related_indicators=None, tags=None, malware_family=None, port=None, internal=None, campaign=None, traffic_light_protocol=None, threat_types=None, asn=None, as_owner=None, geo_country=None, organization=None, community_notes=None, publications=None, relationships=None, - blocked=None, certificates=None, description=None, stix_id=None): + blocked=None, certificates=None, description=None, stix_id=None, organization_prevalence=None, + global_prevalence=None, organization_first_seen=None, organization_last_seen=None, + first_seen_by_source=None, last_seen_by_source=None): # Main indicator value self.url = url @@ -4432,6 +4540,12 @@ def __init__(self, url, dbot_score, detection_engines=None, positive_detections= self.geo_country = geo_country self.organization = organization self.publications = publications + self.organization_prevalence = organization_prevalence + self.global_prevalence = global_prevalence + self.organization_first_seen = organization_first_seen + self.organization_last_seen = organization_last_seen + self.first_seen_by_source = first_seen_by_source + self.last_seen_by_source = last_seen_by_source # XSOAR Fields self.relationships = relationships @@ -4505,6 +4619,24 @@ def to_context(self): if self.publications: url_context['Publications'] = self.create_context_table(self.publications) + if self.organization_prevalence is not None: # checking for `is not None` to allow `0`-value + url_context['OrganizationPrevalence'] = self.organization_prevalence + + if self.global_prevalence is not None: # checking for `is not None` to allow `0`-value + url_context['GlobalPrevalence'] = self.global_prevalence + + if self.organization_first_seen: + url_context['OrganizationFirstSeen'] = self.organization_first_seen + + if self.organization_last_seen: + url_context['OrganizationLastSeen'] = self.organization_last_seen + + if self.first_seen_by_source: + url_context['FirstSeenBySource'] = self.first_seen_by_source + + if self.last_seen_by_source: + url_context['LastSeenBySource'] = self.last_seen_by_source + if self.dbot_score and self.dbot_score.score == Common.DBotScore.BAD: url_context['Malicious'] = { 'Vendor': self.dbot_score.integration_name, @@ -4546,6 +4678,24 @@ class Domain(Indicator): :type dns_records: ``DNSRecord`` :param dns_records: A list of DNS records for the domain. + + :type organization_prevalence: ``int`` + :param organization_prevalence: The number of times the indicator is detected in the organization. + + :type global_prevalence: ``int`` + :param global_prevalence: The number of times the indicator is detected across all organizations. + + :type organization_first_seen: ``str`` + :param organization_first_seen: ISO 8601 date time string; when the indicator was first seen in the organization. + + :type organization_last_seen: ``str`` + :param organization_last_seen: ISO 8601 date time string; the last time a specific organization encountered an indicator. + + :type first_seen_by_source: ``str`` + :param first_seen_by_source: ISO 8601 date time string; when the indicator was first seen by the source vendor. + + :type last_seen_by_source: ``str`` + :param last_seen_by_source: ISO 8601 date time string; when the indicator was last seen by the source vendor. """ CONTEXT_PATH = 'Domain(val.Name && val.Name == obj.Name)' @@ -4560,7 +4710,9 @@ def __init__(self, domain, dbot_score, dns=None, detection_engines=None, positiv community_notes=None, publications=None, geo_location=None, geo_country=None, geo_description=None, tech_country=None, tech_name=None, tech_email=None, tech_organization=None, billing=None, whois_records=None, relationships=None, description=None, stix_id=None, blocked=None, - certificates=None, dns_records=None, rank=None): + certificates=None, dns_records=None, rank=None, organization_prevalence=None, + global_prevalence=None, organization_first_seen=None, organization_last_seen=None, + first_seen_by_source=None, last_seen_by_source=None): # Main indicator value self.domain = domain @@ -4609,7 +4761,7 @@ def __init__(self, domain, dbot_score, dns=None, detection_engines=None, positiv self.tech_email = tech_email self.billing = billing - # Additional custom records (none core) + # Additional custom records (non core) self.domain_status = domain_status self.name_servers = name_servers self.feed_related_indicators = feed_related_indicators @@ -4623,6 +4775,12 @@ def __init__(self, domain, dbot_score, dns=None, detection_engines=None, positiv self.geo_location = geo_location self.geo_country = geo_country self.geo_description = geo_description + self.organization_prevalence = organization_prevalence + self.global_prevalence = global_prevalence + self.organization_first_seen = organization_first_seen + self.organization_last_seen = organization_last_seen + self.first_seen_by_source = first_seen_by_source + self.last_seen_by_source = last_seen_by_source # XSOAR Fields self.relationships = relationships @@ -4707,6 +4865,24 @@ def to_context(self): if self.malware_family: domain_context['MalwareFamily'] = self.malware_family + if self.organization_prevalence is not None: # checking for `is not None` to allow `0`-value + domain_context['OrganizationPrevalence'] = self.organization_prevalence + + if self.global_prevalence is not None: # checking for `is not None` to allow `0`-value + domain_context['GlobalPrevalence'] = self.global_prevalence + + if self.organization_first_seen: + domain_context['OrganizationFirstSeen'] = self.organization_first_seen + + if self.organization_last_seen: + domain_context['OrganizationLastSeen'] = self.organization_last_seen + + if self.first_seen_by_source: + domain_context['FirstSeenBySource'] = self.first_seen_by_source + + if self.last_seen_by_source: + domain_context['LastSeenBySource'] = self.last_seen_by_source + if self.dbot_score and self.dbot_score.score == Common.DBotScore.BAD: domain_context['Malicious'] = { 'Vendor': self.dbot_score.integration_name, diff --git a/Packs/Base/Scripts/CommonServerPython/CommonServerPython_test.py b/Packs/Base/Scripts/CommonServerPython/CommonServerPython_test.py index 533cbfc76bd1..8409428c3dd3 100644 --- a/Packs/Base/Scripts/CommonServerPython/CommonServerPython_test.py +++ b/Packs/Base/Scripts/CommonServerPython/CommonServerPython_test.py @@ -5819,6 +5819,7 @@ def test_create_domain(self): dns='dns.somedomain', detection_engines=10, positive_detections=5, + first_seen_by_source='2024-10-06T09:50:50.555Z', organization='Some Organization', admin_phone='18000000', admin_email='admin@test.com', @@ -5896,6 +5897,7 @@ def test_create_domain(self): 'Registrar': {'Name': 'Mr Registrar', 'AbuseEmail': 'registrar@test.com', 'AbusePhone': None}, 'Registrant': {'Name': 'Mr Registrant', 'Email': None, 'Phone': None, 'Country': None}, 'Admin': {'Name': None, 'Email': 'admin@test.com', 'Phone': '18000000', 'Country': None}, + 'FirstSeenBySource': '2024-10-06T09:50:50.555Z', 'Organization': 'Some Organization', 'Subdomains': ['sub-domain1.somedomain.com', 'sub-domain2.somedomain.com', 'sub-domain3.somedomain.com'], 'DomainStatus': 'ACTIVE', @@ -5994,6 +5996,7 @@ def test_create_url(self): certificates=None, description='description test', stix_id='stix_id', + organization_first_seen='2024-11-04T14:48:23.456Z', ) results = CommandResults( @@ -6033,6 +6036,7 @@ def test_create_url(self): 'ASOwner': 'test_as_owner', 'Geo': {'Country': 'test_geo_country'}, 'Organization': 'test_organization', + 'OrganizationFirstSeen': '2024-11-04T14:48:23.456Z', 'CommunityNotes': [{'note': 'note', 'timestamp': '2019-01-01T00:00:00'}], 'Publications': [ {'source': 'source', @@ -6118,7 +6122,8 @@ def test_create_file(self): creation_date='test_creation_date', description='test_description', hashes=None, - stix_id='test_stix_id' + stix_id='test_stix_id', + organization_prevalence=0, ) results = CommandResults( @@ -6164,6 +6169,7 @@ def test_create_file(self): 'threatcategoryconfidence': 'threat_category_confidence'}], 'Imphash': 'test_imphash', 'Organization': 'test_organization', + 'OrganizationPrevalence': 0, 'Malicious': {'Vendor': 'Test', 'Description': 'malicious!'} } ], diff --git a/Packs/Base/pack_metadata.json b/Packs/Base/pack_metadata.json index 43ef835be4d8..b893de94cb77 100644 --- a/Packs/Base/pack_metadata.json +++ b/Packs/Base/pack_metadata.json @@ -2,7 +2,7 @@ "name": "Base", "description": "The base pack for Cortex XSOAR.", "support": "xsoar", - "currentVersion": "1.34.47", + "currentVersion": "1.35.0", "author": "Cortex XSOAR", "serverMinVersion": "6.0.0", "url": "https://www.paloaltonetworks.com/cortex", diff --git a/Packs/CommonTypes/IndicatorFields/indicatorfield-globalprevalence.json b/Packs/CommonTypes/IndicatorFields/indicatorfield-globalprevalence.json new file mode 100644 index 000000000000..516e9a921e03 --- /dev/null +++ b/Packs/CommonTypes/IndicatorFields/indicatorfield-globalprevalence.json @@ -0,0 +1,36 @@ +{ + "id": "indicator_globalprevalence", + "version": -1, + "modified": "2024-10-30T12:05:32.803043623Z", + "name": "Global Prevalence", + "ownerOnly": false, + "cliName": "globalprevalence", + "type": "number", + "closeForm": false, + "editForm": true, + "required": false, + "neverSetAsRequired": false, + "isReadOnly": false, + "useAsKpi": false, + "locked": false, + "system": false, + "content": true, + "group": 2, + "hidden": false, + "openEnded": false, + "description": "The number of times the indicator is detected across all organizations.", + "associatedTypes": [ + "Domain", + "IP", + "IPv6", + "URL", + "File" + ], + "associatedToAll": false, + "unmapped": false, + "unsearchable": false, + "caseInsensitive": true, + "sla": 0, + "threshold": 72, + "fromVersion": "5.0.0" +} diff --git a/Packs/CommonTypes/IndicatorFields/indicatorfield-organizationfirstseen.json b/Packs/CommonTypes/IndicatorFields/indicatorfield-organizationfirstseen.json new file mode 100644 index 000000000000..b05f4d1e276f --- /dev/null +++ b/Packs/CommonTypes/IndicatorFields/indicatorfield-organizationfirstseen.json @@ -0,0 +1,36 @@ +{ + "id": "indicator_organizationfirstseen", + "version": -1, + "modified": "2024-10-30T12:05:32.803043623Z", + "name": "Organization First Seen", + "ownerOnly": false, + "cliName": "organizationfirstseen", + "type": "date", + "closeForm": false, + "editForm": true, + "required": false, + "neverSetAsRequired": false, + "isReadOnly": false, + "useAsKpi": false, + "locked": false, + "system": false, + "content": true, + "group": 2, + "hidden": false, + "openEnded": false, + "description": "Date and time when the indicator was first seen in the organization.", + "associatedTypes": [ + "Domain", + "IP", + "IPv6", + "URL", + "File" + ], + "associatedToAll": false, + "unmapped": false, + "unsearchable": false, + "caseInsensitive": true, + "sla": 0, + "threshold": 72, + "fromVersion": "5.0.0" +} diff --git a/Packs/CommonTypes/IndicatorFields/indicatorfield-organizationlastseen.json b/Packs/CommonTypes/IndicatorFields/indicatorfield-organizationlastseen.json new file mode 100644 index 000000000000..34ac61c12074 --- /dev/null +++ b/Packs/CommonTypes/IndicatorFields/indicatorfield-organizationlastseen.json @@ -0,0 +1,36 @@ +{ + "id": "indicator_organizationlastseen", + "version": -1, + "modified": "2024-10-30T12:05:32.803043623Z", + "name": "Organization Last Seen", + "ownerOnly": false, + "cliName": "organizationlastseen", + "type": "date", + "closeForm": false, + "editForm": true, + "required": false, + "neverSetAsRequired": false, + "isReadOnly": false, + "useAsKpi": false, + "locked": false, + "system": false, + "content": true, + "group": 2, + "hidden": false, + "openEnded": false, + "description": "Date and time when the indicator was last seen in the organization.", + "associatedTypes": [ + "Domain", + "IP", + "IPv6", + "URL", + "File" + ], + "associatedToAll": false, + "unmapped": false, + "unsearchable": false, + "caseInsensitive": true, + "sla": 0, + "threshold": 72, + "fromVersion": "5.0.0" +} diff --git a/Packs/CommonTypes/IndicatorFields/indicatorfield-organizationprevalence.json b/Packs/CommonTypes/IndicatorFields/indicatorfield-organizationprevalence.json new file mode 100644 index 000000000000..33c64cde6c27 --- /dev/null +++ b/Packs/CommonTypes/IndicatorFields/indicatorfield-organizationprevalence.json @@ -0,0 +1,36 @@ +{ + "id": "indicator_organizationprevalence", + "version": -1, + "modified": "2024-10-30T12:05:32.803043623Z", + "name": "Organization Prevalence", + "ownerOnly": false, + "cliName": "organizationprevalence", + "type": "number", + "closeForm": false, + "editForm": true, + "required": false, + "neverSetAsRequired": false, + "isReadOnly": false, + "useAsKpi": false, + "locked": false, + "system": false, + "content": true, + "group": 2, + "hidden": false, + "openEnded": false, + "description": "The number of times the indicator is detected in the organization.", + "associatedTypes": [ + "Domain", + "IP", + "IPv6", + "URL", + "File" + ], + "associatedToAll": false, + "unmapped": false, + "unsearchable": false, + "caseInsensitive": true, + "sla": 0, + "threshold": 72, + "fromVersion": "5.0.0" +} diff --git a/Packs/CommonTypes/IndicatorTypes/reputation-domain.json b/Packs/CommonTypes/IndicatorTypes/reputation-domain.json index c9ce39df14fe..47408478d4b2 100644 --- a/Packs/CommonTypes/IndicatorTypes/reputation-domain.json +++ b/Packs/CommonTypes/IndicatorTypes/reputation-domain.json @@ -535,6 +535,30 @@ } ] } + }, + "organizationprevalence": { + "simple": "Domain.OrganizationPrevalence", + "complex": null + }, + "globalprevalence": { + "simple": "Domain.GlobalPrevalence", + "complex": null + }, + "organizationfirstseen": { + "simple": "Domain.OrganizationFirstSeen", + "complex": null + }, + "organizationlastseen": { + "simple": "Domain.OrganizationLastSeen", + "complex": null + }, + "firstseenbysource": { + "simple": "Domain.FirstSeenBySource", + "complex": null + }, + "lastseenbysource": { + "simple": "Domain.LastSeenBySource", + "complex": null } }, "manualMapping": null, diff --git a/Packs/CommonTypes/IndicatorTypes/reputation-file.json b/Packs/CommonTypes/IndicatorTypes/reputation-file.json index 00d7078e69b7..dc0332d810a7 100644 --- a/Packs/CommonTypes/IndicatorTypes/reputation-file.json +++ b/Packs/CommonTypes/IndicatorTypes/reputation-file.json @@ -476,6 +476,30 @@ } ] } + }, + "organizationprevalence": { + "simple": "File.OrganizationPrevalence", + "complex": null + }, + "globalprevalence": { + "simple": "File.GlobalPrevalence", + "complex": null + }, + "organizationfirstseen": { + "simple": "File.OrganizationFirstSeen", + "complex": null + }, + "organizationlastseen": { + "simple": "File.OrganizationLastSeen", + "complex": null + }, + "firstseenbysource": { + "simple": "File.FirstSeenBySource", + "complex": null + }, + "lastseenbysource": { + "simple": "File.LastSeenBySource", + "complex": null } }, "manualMapping": null, diff --git a/Packs/CommonTypes/IndicatorTypes/reputation-ip.json b/Packs/CommonTypes/IndicatorTypes/reputation-ip.json index 7950dcafc4be..463fb30d25d2 100644 --- a/Packs/CommonTypes/IndicatorTypes/reputation-ip.json +++ b/Packs/CommonTypes/IndicatorTypes/reputation-ip.json @@ -446,6 +446,30 @@ } ] } + }, + "organizationprevalence": { + "simple": "IP.OrganizationPrevalence", + "complex": null + }, + "globalprevalence": { + "simple": "IP.GlobalPrevalence", + "complex": null + }, + "organizationfirstseen": { + "simple": "IP.OrganizationFirstSeen", + "complex": null + }, + "organizationlastseen": { + "simple": "IP.OrganizationLastSeen", + "complex": null + }, + "firstseenbysource": { + "simple": "IP.FirstSeenBySource", + "complex": null + }, + "lastseenbysource": { + "simple": "IP.LastSeenBySource", + "complex": null } }, "manualMapping": null, diff --git a/Packs/CommonTypes/IndicatorTypes/reputation-ipv6.json b/Packs/CommonTypes/IndicatorTypes/reputation-ipv6.json index b1e31422e088..c29bddf55c6e 100644 --- a/Packs/CommonTypes/IndicatorTypes/reputation-ipv6.json +++ b/Packs/CommonTypes/IndicatorTypes/reputation-ipv6.json @@ -229,6 +229,30 @@ } ] } + }, + "organizationprevalence": { + "simple": "IPv6.OrganizationPrevalence", + "complex": null + }, + "globalprevalence": { + "simple": "IPv6.GlobalPrevalence", + "complex": null + }, + "organizationfirstseen": { + "simple": "IPv6.OrganizationFirstSeen", + "complex": null + }, + "organizationlastseen": { + "simple": "IPv6.OrganizationLastSeen", + "complex": null + }, + "firstseenbysource": { + "simple": "IPv6.FirstSeenBySource", + "complex": null + }, + "lastseenbysource": { + "simple": "IPv6.LastSeenBySource", + "complex": null } }, "manualMapping": null, diff --git a/Packs/CommonTypes/IndicatorTypes/reputation-url.json b/Packs/CommonTypes/IndicatorTypes/reputation-url.json index bde2713d4069..982b2cb33bed 100644 --- a/Packs/CommonTypes/IndicatorTypes/reputation-url.json +++ b/Packs/CommonTypes/IndicatorTypes/reputation-url.json @@ -264,7 +264,31 @@ "operator": "uniq", "args": {} }] - } + } + }, + "organizationprevalence": { + "simple": "URL.OrganizationPrevalence", + "complex": null + }, + "globalprevalence": { + "simple": "URL.GlobalPrevalence", + "complex": null + }, + "organizationfirstseen": { + "simple": "URL.OrganizationFirstSeen", + "complex": null + }, + "organizationlastseen": { + "simple": "URL.OrganizationLastSeen", + "complex": null + }, + "firstseenbysource": { + "simple": "URL.FirstSeenBySource", + "complex": null + }, + "lastseenbysource": { + "simple": "URL.LastSeenBySource", + "complex": null } }, "manualMapping": null, diff --git a/Packs/CommonTypes/ReleaseNotes/3_6_0.md b/Packs/CommonTypes/ReleaseNotes/3_6_0.md new file mode 100644 index 000000000000..c052ba460a34 --- /dev/null +++ b/Packs/CommonTypes/ReleaseNotes/3_6_0.md @@ -0,0 +1,87 @@ + +#### Indicator Fields + +##### New: Organization Prevalence + +Added new number field which represents the number of times the indicator is detected in the organization. + +##### New: Global Prevalence + +Added new number field which represents the number of times the indicator is detected across all organizations. + +##### New: Organization First Seen + +Added new date field which represents when the indicator was first seen in the organization. + +##### New: Organization Last Seen + +Added new date field which represents when the indicator was last seen in the organization. + +#### Indicator Types + +##### File + +Added default mapping to the indicator fields: + +| **CLI Name** | **Context Path** | +| --- | --- | +| `organizationprevalence`| File.OrganizationPrevalence | +| `globalprevalence`| File.GlobalPrevalence | +| `organizationfirstseen`| File.OrganizationFirstSeen | +| `organizationlastseen`| File.OrganizationLastSeen | +| `firstseenbysource`| File.FirstSeenBySource | +| `lastseenbysource`| File.LastSeenBySource | + +##### Domain + +Added default mapping to the indicator fields: + +| **CLI Name** | **Context Path** | +| --- | --- | +| `organizationprevalence`| Domain.OrganizationPrevalence | +| `globalprevalence`| Domain.GlobalPrevalence | +| `organizationfirstseen`| Domain.OrganizationFirstSeen | +| `organizationlastseen`| Domain.OrganizationLastSeen | +| `firstseenbysource`| Domain.FirstSeenBySource | +| `lastseenbysource`| Domain.LastSeenBySource | + +##### URL + +Added default mapping to the indicator fields: + +| **CLI Name** | **Context Path** | +| --- | --- | +| `organizationprevalence`| URL.OrganizationPrevalence | +| `globalprevalence`| URL.GlobalPrevalence | +| `organizationfirstseen`| URL.OrganizationFirstSeen | +| `organizationlastseen`| URL.OrganizationLastSeen | +| `firstseenbysource`| URL.FirstSeenBySource | +| `lastseenbysource`| URL.LastSeenBySource | + + +##### IP + +Added default mapping to the indicator fields: + +| **CLI Name** | **Context Path** | +| --- | --- | +| `organizationprevalence`| IP.OrganizationPrevalence | +| `globalprevalence`| IP.GlobalPrevalence | +| `organizationfirstseen`| IP.OrganizationFirstSeen | +| `organizationlastseen`| IP.OrganizationLastSeen | +| `firstseenbysource`| IP.FirstSeenBySource | +| `lastseenbysource`| IP.LastSeenBySource | + +##### IPv6 + +Added default mapping to the indicator fields: + +| **CLI Name** | **Context Path** | +| --- | --- | +| `organizationprevalence`| IPv6.OrganizationPrevalence | +| `globalprevalence`| IPv6.GlobalPrevalence | +| `organizationfirstseen`| IPv6.OrganizationFirstSeen | +| `organizationlastseen`| IPv6.OrganizationLastSeen | +| `firstseenbysource`| IPv6.FirstSeenBySource | +| `lastseenbysource`| IPv6.LastSeenBySource | + diff --git a/Packs/CommonTypes/pack_metadata.json b/Packs/CommonTypes/pack_metadata.json index 3ffc1b23f590..868c219ff148 100644 --- a/Packs/CommonTypes/pack_metadata.json +++ b/Packs/CommonTypes/pack_metadata.json @@ -2,7 +2,7 @@ "name": "Common Types", "description": "This Content Pack will get you up and running in no-time and provide you with the most commonly used incident & indicator fields and types.", "support": "xsoar", - "currentVersion": "3.5.25", + "currentVersion": "3.6.0", "author": "Cortex XSOAR", "url": "https://www.paloaltonetworks.com/cortex", "email": "", diff --git a/Packs/MicrosoftDefenderAdvancedThreatProtection/Integrations/MicrosoftDefenderAdvancedThreatProtection/MicrosoftDefenderAdvancedThreatProtection.py b/Packs/MicrosoftDefenderAdvancedThreatProtection/Integrations/MicrosoftDefenderAdvancedThreatProtection/MicrosoftDefenderAdvancedThreatProtection.py index 6bedf0bf4096..d25eee600a51 100644 --- a/Packs/MicrosoftDefenderAdvancedThreatProtection/Integrations/MicrosoftDefenderAdvancedThreatProtection/MicrosoftDefenderAdvancedThreatProtection.py +++ b/Packs/MicrosoftDefenderAdvancedThreatProtection/Integrations/MicrosoftDefenderAdvancedThreatProtection/MicrosoftDefenderAdvancedThreatProtection.py @@ -5,6 +5,7 @@ from collections.abc import Callable from CommonServerPython import * import urllib3 +import dataclasses from dateutil.parser import parse from requests import Response from MicrosoftApiModule import * # noqa: E402 @@ -79,6 +80,94 @@ INTEGRATION_NAME = 'Microsoft Defender ATP' +@dataclasses.dataclass +class FileStatisticsAPIParser: + sha1: str + org_prevalence: str + organization_prevalence: int + org_first_seen: str | None # same as 'org_prevalence', but as integer + org_last_seen: str | None + global_prevalence: str + globally_prevalence: int # same as 'global_prevalence', but as integer + global_first_observed: str + global_last_observed: str + top_file_names: list[str] + + @classmethod + def from_raw_response(cls, raw_response: dict): + """Creates an instance from the file stats API raw response body (ignores extra fields, if any). + + Args: + raw_response (dict): File stats API response + + Returns: + FileStatisticsAPIParser + """ + dataclass_field_names = {field.name for field in dataclasses.fields(cls)} + snake_case_response = snakify(raw_response) + return cls(**{key: value for key, value in snake_case_response.items() if key in dataclass_field_names}) + + def to_context_output(self) -> dict: + """Generates context output from an instance of FileStatisticsAPIParser. + + Returns: + dict: context output + """ + return { + 'Sha1': self.sha1, + 'Statistics': assign_params( + **{camelize_string(key): value for key, value in dataclasses.asdict(self).items() if key != 'sha1'} + ) + } + + def to_human_readable(self, file_hash: str) -> str: + """Generates a human readable table from an instance of FileStatisticsAPIParser. + + Args: + file_hash (str): The hash of the file + + Returns: + str: human readable markdown table + """ + table_data = {self.format_for_table(key): value for key, value in dataclasses.asdict(self).items() if key != 'sha1'} + return tableToMarkdown(f'Statistics on {file_hash} file:', table_data, removeNull=True) + + def to_file_indicator(self, file_hash: str) -> Common.File: + """Generates a File indicator object from an instance of FileStatisticsAPIParser. + + Args: + file_hash (str): The hash of the file + + Returns: + Common.File + """ + return Common.File( + dbot_score=Common.DBotScore(file_hash, DBotScoreType.FILE, INTEGRATION_NAME, Common.DBotScore.NONE), + sha1=self.sha1, + organization_prevalence=self.organization_prevalence, + global_prevalence=self.globally_prevalence, + organization_first_seen=self.org_first_seen, + organization_last_seen=self.org_last_seen, + first_seen_by_source=self.global_first_observed, + last_seen_by_source=self.global_last_observed, + ) + + @staticmethod + def format_for_table(field_name: str) -> str: + """Replaces certain words and formats fields from 'snake_case' to 'Space Case'. + + Args: + field_name (str): Name of field in snake_case. + + Returns: + str: Formatted in Space Case with replacements. + """ + replacements = {'globally_': 'global_', 'org_': 'organization_'} + for old_value, new_value in replacements.items(): + field_name = field_name.replace(old_value, new_value) + return pascalToSpace(camelize_string(field_name)) + + class HuntingQueryBuilder: """ERROR MESSAGES""" FILE_ARGS_ERR = 'Please provide at least one file arguments: "file_name", "sha1", "sha256" or "md5".' @@ -3385,42 +3474,24 @@ def get_machine_data(machine): return machine_data -def get_file_statistics_command(client: MsClient, args: dict): +def get_file_statistics_command(client: MsClient, args: dict) -> CommandResults: """Retrieves the statistics on the given file. Returns: - (str, dict, dict). Human readable, context, raw response + CommandResults. """ - file_sha1 = args.get('file_hash') - response = client.get_file_statistics(file_sha1) - file_stat = get_file_statistics_context(response) - human_readable = tableToMarkdown(f'Statistics on {file_sha1} file:', file_stat, removeNull=True) - context_output = { - 'Sha1': file_sha1, - 'Statistics': file_stat - } - entry_context = { - 'MicrosoftATP.FileStatistics(val.Sha1 === obj.Sha1)': context_output - } - return human_readable, entry_context, response + file_hash = args.get('file_hash', '') + response = client.get_file_statistics(file_hash) + file_stats = FileStatisticsAPIParser.from_raw_response(response) - -def get_file_statistics_context(file_stat_response): - """Gets the file statistics response and returns it in context format. - - Returns: - (dict). File statistics context - """ - file_stat = assign_params( - OrgPrevalence=file_stat_response.get('orgPrevalence'), - OrgFirstSeen=file_stat_response.get('orgFirstSeen'), - OrgLastSeen=file_stat_response.get('orgLastSeen'), - GlobalPrevalence=file_stat_response.get('globalPrevalence'), - GlobalFirstObserved=file_stat_response.get('globalFirstObserved'), - GlobalLastObserved=file_stat_response.get('globalLastObserved'), - TopFileNames=file_stat_response.get('topFileNames'), + return CommandResults( + outputs_prefix='MicrosoftATP.FileStatistics', + outputs_key_field='Sha1', + indicator=file_stats.to_file_indicator(file_hash), + readable_output=file_stats.to_human_readable(file_hash), + outputs=file_stats.to_context_output(), + raw_response=response, ) - return file_stat def get_file_alerts_command(client: MsClient, args: dict): @@ -5699,7 +5770,7 @@ def main(): # pragma: no cover return_outputs(*get_domain_machine_command(client, args)) elif command == 'microsoft-atp-get-file-statistics': - return_outputs(*get_file_statistics_command(client, args)) + return_results(get_file_statistics_command(client, args)) elif command == 'microsoft-atp-get-file-alerts': return_outputs(*get_file_alerts_command(client, args)) diff --git a/Packs/MicrosoftDefenderAdvancedThreatProtection/Integrations/MicrosoftDefenderAdvancedThreatProtection/MicrosoftDefenderAdvancedThreatProtection.yml b/Packs/MicrosoftDefenderAdvancedThreatProtection/Integrations/MicrosoftDefenderAdvancedThreatProtection/MicrosoftDefenderAdvancedThreatProtection.yml index a38b1c751d5a..a1ae843f595c 100644 --- a/Packs/MicrosoftDefenderAdvancedThreatProtection/Integrations/MicrosoftDefenderAdvancedThreatProtection/MicrosoftDefenderAdvancedThreatProtection.yml +++ b/Packs/MicrosoftDefenderAdvancedThreatProtection/Integrations/MicrosoftDefenderAdvancedThreatProtection/MicrosoftDefenderAdvancedThreatProtection.yml @@ -1919,8 +1919,11 @@ script: description: The file SHA1 hash. type: String - contextPath: MicrosoftATP.FileStatistics.Statistics.OrgPrevalence - description: The prevalence of the file in the organization. + description: The number of times the file is detected in the organization. type: String + - contextPath: MicrosoftATP.FileStatistics.Statistics.OrganizationPrevalence + description: The number of times the file is detected in the organization. + type: Number - contextPath: MicrosoftATP.FileStatistics.Statistics.OrgFirstSeen description: The first date and time the file was in the organization. type: Date @@ -1928,8 +1931,11 @@ script: description: The last date and time the file was in the organization. type: Date - contextPath: MicrosoftATP.FileStatistics.Statistics.GlobalPrevalence - description: The global prevalence of the file. + description: The number of times the file is detected across all organizations by Microsoft Defender ATP. type: String + - contextPath: MicrosoftATP.FileStatistics.Statistics.GloballyPrevalence + description: The number of times the file is detected across all organizations by Microsoft Defender ATP. + type: Number - contextPath: MicrosoftATP.FileStatistics.Statistics.GlobalFirstObserved description: The global first observation date and time of the file. type: Date @@ -1939,6 +1945,27 @@ script: - contextPath: MicrosoftATP.FileStatistics.Statistics.TopFileNames description: The file's top names. type: String + - contextPath: File.SHA1 + description: The SHA1 hash of the file. + type: String + - contextPath: File.OrganizationPrevalence + description: The number of times the indicator is detected in the organization. + type: Number + - contextPath: File.GlobalPrevalence + description: The number of times the indicator is detected across all organizations by Microsoft Defender ATP. + type: Number + - contextPath: File.OrganizationFirstSeen + description: The date and time when the indicator was first seen in the organization. + type: Date + - contextPath: File.OrganizationLastSeen + description: The date and time when the indicator was last seen in the organization. + type: Date + - contextPath: File.FirstSeenBySource + description: The date and time when the indicator was first seen by Microsoft Defender ATP. + type: Date + - contextPath: File.LastSeenBySource + description: The date and time when the indicator was last seen by Microsoft Defender ATP. + type: Date polling: true - arguments: - description: File SHA1 hash to get statistics on. @@ -5624,7 +5651,7 @@ script: execution: false name: microsoft-atp-auth-reset arguments: [] - dockerimage: demisto/crypto:1.0.0.114611 + dockerimage: demisto/crypto:1.0.0.115419 isfetch: true runonce: false script: '-' diff --git a/Packs/MicrosoftDefenderAdvancedThreatProtection/Integrations/MicrosoftDefenderAdvancedThreatProtection/MicrosoftDefenderAdvancedThreatProtection_test.py b/Packs/MicrosoftDefenderAdvancedThreatProtection/Integrations/MicrosoftDefenderAdvancedThreatProtection/MicrosoftDefenderAdvancedThreatProtection_test.py index 005f50fca135..801b12b5813e 100644 --- a/Packs/MicrosoftDefenderAdvancedThreatProtection/Integrations/MicrosoftDefenderAdvancedThreatProtection/MicrosoftDefenderAdvancedThreatProtection_test.py +++ b/Packs/MicrosoftDefenderAdvancedThreatProtection/Integrations/MicrosoftDefenderAdvancedThreatProtection/MicrosoftDefenderAdvancedThreatProtection_test.py @@ -6,12 +6,13 @@ import demistomock as demisto import json import pytest +import dataclasses -from CommonServerPython import DemistoException +from CommonServerPython import snakify, DemistoException from MicrosoftDefenderAdvancedThreatProtection import MsClient, get_future_time, build_std_output, get_machine_by_ip_command, \ parse_ip_addresses, \ print_ip_addresses, get_machine_details_command, run_polling_command, run_live_response_script_action, \ - get_live_response_file_action, put_live_response_file_action, HuntingQueryBuilder, assign_params, \ + get_live_response_file_action, put_live_response_file_action, HuntingQueryBuilder, FileStatisticsAPIParser, assign_params, \ get_machine_users_command, get_machine_alerts_command, get_advanced_hunting_command, create_filters_conjunction, \ create_filters_disjunctions, create_filter, MICROSOFT_DEFENDER_FOR_ENDPOINT_API @@ -383,6 +384,20 @@ def test_check_limit_and_offset_values_limit_zero(mocker): "isOnlyNetworkUser": "false" } +FILE_STATISTICS_API_RESPONSE = { + '@odata.context': 'https://api.security.microsoft.com/api/$metadata#microsoft.windowsDefenderATP.api.InOrgFileStats', + 'sha1': '0991a395da64e1c5fbe8732ed11e6be064081d9f', + 'orgPrevalence': '14850', + 'organizationPrevalence': 14850, # same as 'orgPrevalence', but as integer + 'orgFirstSeen': '2019-12-07T13:44:16Z', + 'orgLastSeen': '2020-01-06T13:39:36Z', + 'globalPrevalence': '705012', + 'globallyPrevalence': 705012, # same as 'globalPrevalence', but as integer + 'globalFirstObserved': '2015-03-19T12:20:07.3432441Z', + 'globalLastObserved': '2020-01-06T13:39:36Z', + 'topFileNames': ['MREC.exe'] +} + USER_DATA = { 'ID': "test/user1", 'AccountName': "user1", @@ -2924,3 +2939,168 @@ def test_generate_login_url(mocker): f'&client_id={client_id}&redirect_uri={redirect_uri})' res = MicrosoftDefenderAdvancedThreatProtection.return_results.call_args[0][0].readable_output assert expected_url in res + + +def test_get_file_statistics_command(mocker): + """ + Given: + - SHA1 File hash + + When: + - Calling the get_file_statistics_command function + + Then: + - Assert correct context output and raw response + """ + from MicrosoftDefenderAdvancedThreatProtection import get_file_statistics_command + + # Set + response = FILE_STATISTICS_API_RESPONSE + mocker.patch.object(client_mocker, 'get_file_statistics', return_value=response) + + # Arrange + results = get_file_statistics_command(client_mocker, {'file_hash': '0991a395da64e1c5fbe8732ed11e6be064081d9f'}) + context_output = results.outputs + + assert context_output['Sha1'] == response['sha1'] + assert context_output['Statistics'] == { + 'OrgPrevalence': response['orgPrevalence'], + 'OrganizationPrevalence': response['organizationPrevalence'], + 'OrgFirstSeen': response['orgFirstSeen'], + 'OrgLastSeen': response['orgLastSeen'], + 'GlobalPrevalence': response['globalPrevalence'], + 'GloballyPrevalence': response['globallyPrevalence'], + 'GlobalFirstObserved': response['globalFirstObserved'], + 'GlobalLastObserved': response['globalLastObserved'], + 'TopFileNames': response['topFileNames'], + } + + assert results.raw_response == response + + +@pytest.fixture +def file_stats(): + """Fixture to create a FileStatisticsAPIParser instance.""" + return FileStatisticsAPIParser.from_raw_response(FILE_STATISTICS_API_RESPONSE) + + +def test_file_statistics_api_parser_from_raw_response(file_stats: FileStatisticsAPIParser): + """ + Given: + - An instance of FileStatisticsAPIParser created from file statistics API response + + When: + - Casting the FileStatisticsAPIParser dataclass to a dictionary + + Then: + - Assert no excluded fields in dictionary + - Assert all relevant fields in dictionary + """ + # Set + response = FILE_STATISTICS_API_RESPONSE + excluded_key = '@odata.context' + + # Arrange + file_stats_dict = dataclasses.asdict(file_stats) + snake_case_response = snakify(response) + + # Assert + assert excluded_key not in file_stats_dict + assert file_stats_dict == {key: value for key, value in snake_case_response.items() if key != excluded_key} + + +def test_file_statistics_api_parser_to_context(file_stats: FileStatisticsAPIParser): + """ + Given: + - An instance of FileStatisticsAPIParser created from file statistics API response + + When: + - Calling the FileStatisticsAPIParser.to_context_output method + + Then: + - Assert correct context output + """ + # Set + response = FILE_STATISTICS_API_RESPONSE + + # Arrange + context_output = file_stats.to_context_output() + + # Assert + assert context_output['Sha1'] == response['sha1'] + assert context_output['Statistics'] == { + 'OrgPrevalence': response['orgPrevalence'], + 'OrganizationPrevalence': response['organizationPrevalence'], + 'OrgFirstSeen': response['orgFirstSeen'], + 'OrgLastSeen': response['orgLastSeen'], + 'GlobalPrevalence': response['globalPrevalence'], + 'GloballyPrevalence': response['globallyPrevalence'], + 'GlobalFirstObserved': response['globalFirstObserved'], + 'GlobalLastObserved': response['globalLastObserved'], + 'TopFileNames': response['topFileNames'], + } + + +def test_file_statistics_api_parser_to_file_indicator(file_stats: FileStatisticsAPIParser): + """ + Given: + - SHA1 file hash and an instance FileStatisticsAPIParser created from file statistics API response + + When: + - Calling the FileStatisticsAPIParser.to_file_indicator method + + Then: + - Assert correct human readable table name and data + """ + # Set + file_hash = '0991a395da64e1c5fbe8732ed11e6be064081d9f' + response = FILE_STATISTICS_API_RESPONSE + + # Arrange + file_indicator = file_stats.to_file_indicator(file_hash) + indicator_data: dict = next(iter(file_indicator.to_context().values())) + indicator_data.pop('Hashes', None) # generated by Common.File, irrelevant in this unit test + + # Assert + assert indicator_data == { + 'SHA1': response['sha1'], + 'OrganizationPrevalence': response['organizationPrevalence'], + 'GlobalPrevalence': response['globallyPrevalence'], + 'OrganizationFirstSeen': response['orgFirstSeen'], + 'OrganizationLastSeen': response['orgLastSeen'], + 'FirstSeenBySource': response['globalFirstObserved'], + 'LastSeenBySource': response['globalLastObserved'], + } + + +def test_file_statistics_api_parser_to_human_readable(mocker, file_stats: FileStatisticsAPIParser): + """ + Given: + - SHA1 file hash and an instance FileStatisticsAPIParser created from file statistics API response + + When: + - Calling the FileStatisticsAPIParser.to_human_readable method + + Then: + - Assert correct human readable table name and data + """ + # Set + file_hash = '0991a395da64e1c5fbe8732ed11e6be064081d9f' + response = FILE_STATISTICS_API_RESPONSE + table_to_markdown = mocker.patch('MicrosoftDefenderAdvancedThreatProtection.tableToMarkdown') + + # Arrange + file_stats.to_human_readable(file_hash) + table_name, table_data = table_to_markdown.call_args[0] + + # Assert + assert table_name == f'Statistics on {file_hash} file:' + assert table_data == { + 'Organization Prevalence': response['organizationPrevalence'], + 'Organization First Seen': response['orgFirstSeen'], + 'Organization Last Seen': response['orgLastSeen'], + 'Global Prevalence': response['globallyPrevalence'], + 'Global First Observed': response['globalFirstObserved'], + 'Global Last Observed': response['globalLastObserved'], + 'Top File Names': response['topFileNames'], + } diff --git a/Packs/MicrosoftDefenderAdvancedThreatProtection/Integrations/MicrosoftDefenderAdvancedThreatProtection/README.md b/Packs/MicrosoftDefenderAdvancedThreatProtection/Integrations/MicrosoftDefenderAdvancedThreatProtection/README.md index 1809d5d2ca70..1e4fd6ff54df 100644 --- a/Packs/MicrosoftDefenderAdvancedThreatProtection/Integrations/MicrosoftDefenderAdvancedThreatProtection/README.md +++ b/Packs/MicrosoftDefenderAdvancedThreatProtection/Integrations/MicrosoftDefenderAdvancedThreatProtection/README.md @@ -2515,14 +2515,22 @@ File.Read.All | **Path** | **Type** | **Description** | | --- | --- | --- | | MicrosoftATP.FileStatistics.Sha1 | String | The file SHA1 hash. | -| MicrosoftATP.FileStatistics.Statistics.OrgPrevalence | String | The prevalence of the file in the organization. | +| MicrosoftATP.FileStatistics.Statistics.OrgPrevalence | String | The number of times the file is detected in the organization. | +| MicrosoftATP.FileStatistics.Statistics.OrganizationPrevalence | Number | The number of times the file is detected in the organization. | | MicrosoftATP.FileStatistics.Statistics.OrgFirstSeen | Date | The first date and time the file was seen in the organization. | | MicrosoftATP.FileStatistics.Statistics.OrgLastSeen | Date | The last date and time the file was seen in the organization. | -| MicrosoftATP.FileStatistics.Statistics.GlobalPrevalence | String | The global prevalence of the file. | +| MicrosoftATP.FileStatistics.Statistics.GlobalPrevalence | String | The number of times the file is detected across all organizations by Microsoft Defender ATP. | +| MicrosoftATP.FileStatistics.Statistics.GloballyPrevalence | Number | The number of times the file is detected across all organizations by Microsoft Defender ATP. | | MicrosoftATP.FileStatistics.Statistics.GlobalFirstObserved | Date | The first global observation date and time of the file. | | MicrosoftATP.FileStatistics.Statistics.GlobalLastObserved | Date | The last global observation date and time of the file. | | MicrosoftATP.FileStatistics.Statistics.TopFileNames | String | The top names of the file. | - +| File.SHA1 | String | The SHA1 hash of the file. | +| File.OrganizationPrevalence | Number | The number of times the indicator is detected in the organization. | +| File.GlobalPrevalence | Number | The number of times the indicator is detected across all organizations by Microsoft Defender ATP. | +| File.OrganizationFirstSeen | Date | The date and time when the indicator was first seen in the organization. | +| File.OrganizationLastSeen | Date | The date and time when the indicator was last seen in the organization. | +| File.FirstSeenBySource | Date | The date and time when the indicator was first seen by Microsoft Defender ATP. | +| File.LastSeenBySource | Date | The date and time when the indicator was last seen by Microsoft Defender ATP. | ##### Command Example @@ -2532,16 +2540,33 @@ File.Read.All ```json { - "MicrosoftATP.FileStatistics": { - "Sha1": "9fe3ba25e5660c23dfe478d577cfacde5795870c", - "Statistics": { - "TopFileNames": [ - "lsass.exe" - ], - "GlobalFirstObserved": "2019-04-03T04:10:18.1001071Z", - "GlobalPrevalence": "1355899", - "OrgPrevalence": "0", - "GlobalLastObserved": "2020-03-23T09:24:54.169574Z" + "File": { + "SHA1": "9fe3ba25e5660c23dfe478d577cfacde5795870c", + "FirstSeenBySource": "2019-04-03T04:10:18.1001071Z", + "LastSeenBySource": "2020-03-23T09:24:54.169574Z", + "GlobalPrevalence": 1355899, + "Hashes":[ + { + "type" :"SHA1", + "value": "9fe3ba25e5660c23dfe478d577cfacde5795870c" + } + ], + "OrganizationPrevalence": 0 + }, + "MicrosoftATP": { + "FileStatistics": { + "Sha1": "9fe3ba25e5660c23dfe478d577cfacde5795870c", + "Statistics": { + "TopFileNames": [ + "lsass.exe" + ], + "GlobalFirstObserved": "2019-04-03T04:10:18.1001071Z", + "GlobalPrevalence": "1355899", + "GloballyPrevalence": 1355899, + "OrgPrevalence": "0", + "OrganizationPrevalence": 0, + "GlobalLastObserved": "2020-03-23T09:24:54.169574Z" + } } } } @@ -2551,11 +2576,17 @@ File.Read.All ##### Statistics on 9fe3ba25e5660c23dfe478d577cfacde5795870c file: -|GlobalFirstObserved|GlobalLastObserved|GlobalPrevalence|OrgPrevalence|TopFileNames| +|Global First Observed|Global Last Observed|Global Prevalence|Organization Prevalence|Top File Names| |---|---|---|---|---| | 2019-04-03T04:10:18.1001071Z | 2020-03-23T09:24:54.169574Z | 1355899 | 0 | lsass.exe | +##### File Indicator Example + +| Type | Value | Verdict | Related Incidents | Expiration | Global Prevalence | Organization Prevalence | First Seen By Source | Last Seen By Source | Organization First Seen | Organization Last Seen | +|---|---|---|---|---|---|---|---|---|---|---| +| File | 50ef7c645fd5cbb95d50fbaddf6213800f9296ec | Benign | 2 | Never | 195803 | 0 | April 03, 2019 4:10 AM | March 23, 2020 9:24 AM | N/A | N/A | + ### 27. microsoft-atp-get-file-alerts --- diff --git a/Packs/MicrosoftDefenderAdvancedThreatProtection/ReleaseNotes/1_17_0.md b/Packs/MicrosoftDefenderAdvancedThreatProtection/ReleaseNotes/1_17_0.md new file mode 100644 index 000000000000..e4a8a16378b6 --- /dev/null +++ b/Packs/MicrosoftDefenderAdvancedThreatProtection/ReleaseNotes/1_17_0.md @@ -0,0 +1,13 @@ + +#### Integrations + +##### Microsoft Defender for Endpoint + +The ***microsoft-atp-get-file-statistics*** command now returns a File indicator with the following fields: + +- **New: Organization Prevalence** +- **New: Global Prevalence** +- **New: Organization First Seen** +- **New: Organization Last Seen** +- **First Seen By Source** +- **Last Seen By Source** diff --git a/Packs/MicrosoftDefenderAdvancedThreatProtection/pack_metadata.json b/Packs/MicrosoftDefenderAdvancedThreatProtection/pack_metadata.json index 4465794a6909..eabbfc48d00d 100644 --- a/Packs/MicrosoftDefenderAdvancedThreatProtection/pack_metadata.json +++ b/Packs/MicrosoftDefenderAdvancedThreatProtection/pack_metadata.json @@ -2,7 +2,7 @@ "name": "Microsoft Defender for Endpoint", "description": "Microsoft Defender for Endpoint (previously Microsoft Defender Advanced Threat Protection (ATP)) is a unified platform for preventative protection, post-breach detection, automated investigation, and response.", "support": "xsoar", - "currentVersion": "1.16.44", + "currentVersion": "1.17.0", "author": "Cortex XSOAR", "url": "https://www.paloaltonetworks.com/cortex", "email": "",