def validate(self): """Validates this IOC structure's state. :raise InvalidObjectError: if the IOC structure's state is invalid """ super(IOC, self).validate() for md5 in self.md5: if not validators(md5): raise InvalidObjectError( "invalid MD5 checksum: {}".format(md5)) for ipv4 in self.ipv4: if not validators(ipv4): raise InvalidObjectError( "invalid IPv4 address: {}".format(ipv4)) for ipv6 in self.ipv6: if not validators(ipv6): raise InvalidObjectError( "invalid IPv6 address: {}".format(ipv6)) for dns in self.dns: if not validators(dns): raise InvalidObjectError("invalid domain: {}".format(dns)) for query in self.query: if not self._cb.validate(query["search_query"]): raise InvalidObjectError("invalid search query: {}".format( query["search_query"]))
def delete(self): """Deletes this report from the ThreatHunter server. >>> report.delete() :raises InvalidObjectError: if `id` is missing, or `feed_id` is missing and this report is a feed report """ if not self.id: raise InvalidObjectError("missing Report ID") if self._from_watchlist: url = "/threathunter/watchlistmgr/v3/orgs/{}/reports/{}".format( self._cb.credentials.org_key, self.id ) else: if not self._feed_id: raise InvalidObjectError("missing Feed ID") url = "/threathunter/feedmgr/v2/orgs/{}/feeds/{}/reports/{}".format( self._cb.credentials.org_key, self._feed_id, self.id ) self._cb.delete_object(url)
def custom_severity(self, sev_level): """Sets or removed the custom severity for this report :param int sev_level: the new severity, or None to remove the custom severity :return: The new custom severity, or None if removed :rtype: :py:class:`ReportSeverity` or None :raise InvalidObjectError: if `id` is missing or this report is from a watchlist """ if not self.id: raise InvalidObjectError("missing report ID") if self._from_watchlist: raise InvalidObjectError( "watchlist reports don't have custom severities") url = "/threathunter/watchlistmgr/v3/orgs/{}/reports/{}/severity".format( self._cb.credentials.org_key, self.id) if sev_level is None: self._cb.delete_object(url) return args = { "report_id": self.id, "severity": sev_level, } resp = self._cb.put_object(url, args).json() return ReportSeverity(self._cb, initial_data=resp)
def unignore(self): """Removes the ignore status on this IOC. Only watchlist IOCs have an ignore status. :raises InvalidObjectError: if `id` is missing or this IOC is not from a watchlist """ if not self.id: raise InvalidObjectError("missing Report ID") if not self._report_id: raise InvalidObjectError("ignoring only applies to watchlist IOCs") url = "/threathunter/watchlistmgr/v1/report/{}/ioc/{}/ignore".format(self._report_id, self.id) self._cb.delete_object(url)
def custom_severity(self): """Returns the custom severity for this report. :return: The custom severity for this report, if it exists :rtype: :py:class:`ReportSeverity` :raise InvalidObjectError: if `id` is missing or this report is from a watchlist """ if not self.id: raise InvalidObjectError("missing report ID") if self._from_watchlist: raise InvalidObjectError("watchlist reports don't have custom severities") resp = self._cb.get_object("/threathunter/watchlistmgr/v1/severity/report/{}".format(self.id)) return ReportSeverity(self._cb, initial_data=resp)
def unignore(self): """Removes the ignore status on this report. Only watchlist reports have an ignore status. :raises InvalidObjectError: if `id` is missing or this report is not from a watchlist """ if not self.id: raise InvalidObjectError("missing Report ID") if not self._from_watchlist: raise InvalidObjectError("ignoring only applies to watchlist reports") self._cb.delete_object("/threathunter/watchlistmgr/v1/report/{}/ignore".format(self.id))
def ignore(self): """Sets the ignore status on this IOC. Only watchlist IOCs have an ignore status. :raises InvalidObjectError: if `id` is missing or this IOC is not from a watchlist """ if not self.id: raise InvalidObjectError("missing Report ID") if not self._report_id: raise InvalidObjectError("ignoring only applies to watchlist IOCs") url = "/threathunter/watchlistmgr/v3/orgs/{}/reports/{}/iocs/{}/ignore".format( self._cb.credentials.org_key, self._report_id, self.id) self._cb.put_object(url, None)
def validate(self): """Validates this feed's state. :raise InvalidObjectError: if the feed's state is invalid """ super(Feed, self).validate() if self.access not in ["public", "private"]: raise InvalidObjectError("access should be public or private") if not validators.url(self.provider_url): raise InvalidObjectError("provider_url should be a valid URL") for report in self._reports: report.validate()
def update(self, **kwargs): """Update this feed's metadata with the given arguments. >>> feed.update(access="private") :param kwargs: The fields to update :type kwargs: dict(str, str) :raise InvalidObjectError: if `id` is missing or :py:meth:`validate` fails :raise ApiError: if an invalid field is specified """ if not self.id: raise InvalidObjectError("missing feed ID") for key, value in kwargs.items(): if key in self._info: self._info[key] = value self.validate() url = "/threathunter/feedmgr/v2/orgs/{}/feeds/{}/feedinfo".format( self._cb.credentials.org_key, self.id, ) new_info = self._cb.put_object(url, self._info).json() self._info.update(new_info) return self
def update(self, **kwargs): """Updates this watchlist with the given arguments. >>> watchlist.update(name="New Name") :param kwargs: The fields to update :type kwargs: dict(str, str) :raise InvalidObjectError: if `id` is missing or :py:meth:`validate` fails :raise ApiError: if `report_ids` is given *and* is empty """ if not self.id: raise InvalidObjectError("missing Watchlist ID") # NOTE(ww): Special case, according to the docs. if "report_ids" in kwargs and not kwargs["report_ids"]: raise ApiError( "can't update a watchlist to have an empty report list") for key, value in kwargs.items(): if key in self._info: self._info[key] = value self.validate() url = "/threathunter/watchlistmgr/v3/orgs/{}/watchlists/{}".format( self._cb.credentials.org_key, self.id) new_info = self._cb.put_object(url, self._info).json() self._info.update(new_info)
def update(self, **kwargs): """Update this report with the given arguments. .. NOTE:: The report's timestamp is always updated, regardless of whether passed explicitly. >>> report.update(title="My new report title") :param kwargs: The fields to update :type kwargs: dict(str, str) :return: The updated report :rtype: :py:class:`Report` :raises InvalidObjectError: if `id` is missing, or `feed_id` is missing and this report is a feed report, or :py:meth:`validate` fails """ if not self.id: raise InvalidObjectError("missing Report ID") if self._from_watchlist: url = "/threathunter/watchlistmgr/v3/orgs/{}/reports/{}".format( self._cb.credentials.org_key, self.id ) else: if not self._feed_id: raise InvalidObjectError("missing Feed ID") url = "/threathunter/feedmgr/v2/orgs/{}/feeds/{}/reports/{}".format( self._cb.credentials.org_key, self._feed_id, self.id ) for key, value in kwargs.items(): if key in self._info: self._info[key] = value # NOTE(ww): Updating reports on the watchlist API appears to require # updated timestamps. self.timestamp = int(time.time()) self.validate() new_info = self._cb.put_object(url, self._info).json() self._info.update(new_info) return self
def enable_alerts(self): """Enable alerts for this watchlist. Alerts are not retroactive. :raise InvalidObjectError: if `id` is missing """ if not self.id: raise InvalidObjectError("missing Watchlist ID") self._cb.put_object("/threathunter/watchlistmgr/v1/watchlist/{}/alert".format(self.id), None)
def delete(self): """Deletes this watchlist from the ThreatHunter server. :raise InvalidObjectError: if `id` is missing """ if not self.id: raise InvalidObjectError("missing Watchlist ID") self._cb.delete_object("/threathunter/watchlistmgr/v1/watchlist/{}".format(self.id))
def delete(self): """Deletes this feed from the ThreatHunter server. :raise InvalidObjectError: if `id` is missing """ if not self.id: raise InvalidObjectError("missing feed ID") self._cb.delete_object("/threathunter/feedmgr/v1/feed/{}".format(self.id))
def ignored(self): """Returns whether or not this IOC is ignored >>> if ioc.ignored: ... ioc.unignore() :return: the ignore status :rtype: bool :raise InvalidObjectError: if this IOC is missing an `id` or is not a watchlist IOC """ if not self.id: raise InvalidObjectError("missing IOC ID") if not self._report_id: raise InvalidObjectError("ignore status only applies to watchlist IOCs") url = "/threathunter/watchlistmgr/v1/report/{}/ioc/{}/ignore".format(self._report_id, self.id) resp = self._cb.get_object(url) return resp["ignored"]
def disable_tags(self): """Disable tagging for this watchlist. :raise InvalidObjectError: if `id` is missing """ if not self.id: raise InvalidObjectError("missing Watchlist ID") self._cb.delete_object("/threathunter/watchlistmgr/v1/watchlist/{}/tag".format(self.id))
def validate(self): """Validates this IOC_V2's state. :raise InvalidObjectError: if the IOC_V2's state is invalid """ super(IOC_V2, self).validate() if self.link and not validators.url(self.link): raise InvalidObjectError("link should be a valid URL")
def ignored(self): """Returns the ignore status for this report. Only watchlist reports have an ignore status. >>> if report.ignored: ... report.unignore() :return: whether or not this report is ignored :rtype: bool :raises InvalidObjectError: if `id` is missing or this report is not from a watchlist """ if not self.id: raise InvalidObjectError("missing Report ID") if not self._from_watchlist: raise InvalidObjectError("ignore status only applies to watchlist reports") resp = self._cb.get_object("/threathunter/watchlistmgr/v1/report/{}/ignore".format(self.id)) return resp["ignored"]
def delete(self): """Deletes this feed from the ThreatHunter server. :raise InvalidObjectError: if `id` is missing """ if not self.id: raise InvalidObjectError("missing feed ID") url = "/threathunter/feedmgr/v2/orgs/{}/feeds/{}".format( self._cb.credentials.org_key, self.id) self._cb.delete_object(url)
def disable_tags(self): """Disable tagging for this watchlist. :raise InvalidObjectError: if `id` is missing """ if not self.id: raise InvalidObjectError("missing Watchlist ID") url = "/threathunter/watchlistmgr/v3/orgs/{}/watchlists/{}/tag".format( self._cb.credentials.org_key, self.id) self._cb.delete_object(url)
def enable_alerts(self): """Enable alerts for this watchlist. Alerts are not retroactive. :raise InvalidObjectError: if `id` is missing """ if not self.id: raise InvalidObjectError("missing Watchlist ID") url = "/threathunter/watchlistmgr/v3/orgs/{}/watchlists/{}/alert".format( self._cb.credentials.org_key, self.id) self._cb.put_object(url, None)
def delete(self): """Deletes this watchlist from the ThreatHunter server. :raise InvalidObjectError: if `id` is missing """ if not self.id: raise InvalidObjectError("missing Watchlist ID") url = "/threathunter/watchlistmgr/v3/orgs/{}/watchlists/{}".format( self._cb.credentials.org_key, self.id) self._cb.delete_object(url)
def validate(self): """Validates this report's state. :raise InvalidObjectError: if the report's state is invalid """ super(Report, self).validate() if self.link and not validators.url(self.link): raise InvalidObjectError("link should be a valid URL") if self.iocs_v2: [ioc.validate() for ioc in self._iocs_v2]
def replace_reports(self, reports): """Replace this feed's reports with the given reports. :param reports: the reports to replace with :type reports: list(:py:class:`Report`) :raise InvalidObjectError: if `id` is missing """ if not self.id: raise InvalidObjectError("missing feed ID") rep_dicts = [report._info for report in reports] body = {"reports": rep_dicts} self._cb.post_object("/threathunter/feedmgr/v1/{}/report".format(self.id), body)
def append_reports(self, reports): """Append the given reports to this feed's current reports. :param reports: the reports to append :type reports: list(:py:class:`Report`) :raise InvalidObjectError: if `id` is missing """ if not self.id: raise InvalidObjectError("missing feed ID") rep_dicts = [report._info for report in reports] rep_dicts += [report._info for report in self._reports] body = {"reports": rep_dicts} self._cb.post_object("/threathunter/feedmgr/v1/{}/report".format(self.id), body)
def download_url(self, expiration_seconds=3600): """Returns a URL that can be used to download the file for this binary. Returns None if no download can be found. :param expiration_seconds: How long the download should be valid for :raise InvalidObjectError: if URL retrieval should be retried :return: A pre-signed AWS download URL :rtype: str """ downloads = self._cb.select(Downloads, [self.sha256], expiration_seconds=expiration_seconds) if self.sha256 in downloads.not_found: return None elif self.sha256 in downloads.error: raise InvalidObjectError("{} should be retried".format(self.sha256)) else: return next((item.url for item in downloads.found() if self.sha256 == item.sha256), None)