From aa60b0fa9fdb5b4947aa9b5e6771e75cf7b40a91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Clark=20Pa=C3=B1ares?= Date: Wed, 19 Aug 2026 19:28:35 +0800 Subject: [PATCH] add all available parameters for feeds in CLI --- README.md | 43 +++ domaintools/api.py | 254 +++++++++++++---- domaintools/cli/commands/feeds.py | 434 ++++++++++++++++++++++++------ 3 files changed, 596 insertions(+), 135 deletions(-) diff --git a/README.md b/README.md index e7a2077..c9a6a12 100644 --- a/README.md +++ b/README.md @@ -284,6 +284,49 @@ The Feed API standard access pattern is to periodically request the most recent - Either an `after=-60` query parameter, where (in this example) -60 indicates the previous 60 seconds. - Or `after` and `before` query parameters for a time range, with each parameter accepting an ISO-8601 UTC formatted timestamp (a UTC date and time of the format YYYY-MM-DDThh:mm:ssZ) +### Feed parameters + +The feed methods accept the following parameters, grouped by purpose. Availability depends on the feed (see the notes below the table). + +#### Session Management Parameters + +- `sessionID`: A custom string used to distinguish between different sessions. Required when using `fromBeginning`. +- `after`: Start of the query window. Either an integer offset relative to now in seconds (e.g. `-60`), or an absolute ISO 8601 UTC datetime (`YYYY-MM-DDTHH:MM:SSZ`). +- `before`: End of the query window (inclusive). Either an integer from `-1` to `-432000` (seconds before now), or an absolute ISO 8601 UTC datetime. The query window covers at most the most recent 5 days; a value older than 5 days returns no records. +- `fromBeginning`: Boolean (`true`/`false`/`1`/`0`, default `false`). Requires a valid `sessionID`. When `true` on the first request of a new session, returns the first hour of data in the time window instead of the last. Using it with an existing `sessionID` returns an HTTP 406; using it without a `sessionID` or with a non-boolean value returns an HTTP 422. + + ```python + api = API(USERNAME, KEY) + api.nod(sessionID="my-new-session-id", after=-3600, fromBeginning=True) + ``` + +#### Filter Parameters + +- `domain`: Filter for an exact domain or a substring contained within a domain by prefixing or suffixing your substring with `*`. +- `overall_min`, `malware_min`, `phishing_min`, `spam_min`, `proximity_min`: Integer risk score thresholds (range `1` to `99`, optional). Available on the `realtime_domain_risk` and `domainhotlist` feeds only. When multiple are supplied they act as a logical AND — a domain must meet ALL specified thresholds to be returned. + + ```python + api = API(USERNAME, KEY) + api.domainhotlist(after=-3600, overall_min=70, phishing_min=50) + ``` + +- IP feed filters (available on the `iprisk` and `iphotlist` feeds only). All are optional integers/strings and combine as a logical AND: + - Domain activity & volume: `pdns_resolutions_min`, `bad_pdns_resolutions_min` (positive integers, distinct/bad domains resolving to the IP in the last 24 hours) and `total_domains_max` (positive integer; caps total hosted domains to filter out superhosters like CDNs). + - Threat intelligence & combined risk percentages: `third_party_threats_min` (positive integer), plus `all_threats_combined_percent_min`, `combined_phishing_percent_min`, `combined_malware_percent_min`, `combined_spam_percent_min` (percentages `0` to `100` of hosted domains confirmed or predicted malicious). + - Confirmed threat percentages: `all_threats_percent_min`, `percent_phishing_min`, `percent_malware_min`, `percent_spam_min` (percentages `0` to `100` of hosted domains actively confirmed). + - Infrastructure & geolocation: `asn` (integer, digits only — no `AS` prefix or wildcards), `organization` (exact name, no wildcards) and `country_code` (case-sensitive two-letter code, e.g. `CN`, `US`, `NL`). + + ```python + api = API(USERNAME, KEY) + api.iprisk(after=-3600, bad_pdns_resolutions_min=5, total_domains_max=1000, country_code="US") + ``` + +#### Result formatting parameters + +- `output_format`: `csv` or `jsonl` (default `jsonl`). Not available on the `domainrdap` feed. `csv` is not available for `download` endpoints. +- `headers`: When `csv` output is used, adds a header row to the first line of the response. +- `top`: Positive integer from `1` to `1,000,000,000` limiting the number of results in the response payload. + ## Handling iterative response from RTUF endpoints: Since we may dealing with large feeds datasets, the python wrapper uses `generator` for efficient memory handling. Therefore, we need to iterate through the `generator` if we're accessing the partial results of the feeds data. diff --git a/domaintools/api.py b/domaintools/api.py index 529ba48..9d6dd3b 100644 --- a/domaintools/api.py +++ b/domaintools/api.py @@ -1181,17 +1181,25 @@ def nod(self, **kwargs) -> FeedsResults: """Returns back list of the newly observed domains feed. Apex-level domains (e.g. example.com but not www.example.com) that we observe for the first time, and have not observed previously with our global DNS sensor network. - domain: str: Filter for an exact domain or a substring contained within a domain by prefixing or suffixing your substring with "*". Check the documentation for examples + # Session Management Parameters - before: str: Filter for records before the given time value inclusive or time offset relative to now + sessionID: str: A custom string to distinguish between different sessions. Required when using fromBeginning. - after: str: Filter for records after the given time value inclusive or time offset relative to now + after: str: Start of the query window. Either an integer offset relative to now in seconds, or an absolute ISO 8601 UTC datetime (YYYY-MM-DDTHH:MM:SSZ). - headers: bool: Use in combination with Accept: text/csv headers to control if headers are sent or not + before: str: End of the query window (inclusive). Either an integer from -1 to -432000 (seconds before now), or an absolute ISO 8601 UTC datetime (YYYY-MM-DDTHH:MM:SSZ). The query window covers at most the most recent 5 days; a value older than 5 days returns no records. - sessionID: str: A custom string to distinguish between different sessions + fromBeginning: bool: Requires a valid sessionID. When true on the first request of a new session, returns the first hour of data in the time window instead of the last. Using it with an existing sessionID returns HTTP 406; using it without a sessionID or with a non-boolean value returns HTTP 422. - top: int: Limit the number of results to the top N, where N is the value of this parameter. + # Filter Parameters + + domain: str: Filter for an exact domain or a substring contained within a domain by prefixing or suffixing your substring with "*". Check the documentation for examples + + # Result formatting parameters + + headers: bool: Use in combination with Accept: text/csv headers to control if headers are sent or not + + top: int: Limit the number of results to the top N, where N is a positive integer from 1 to 1,000,000,000. """ validate_feeds_parameters(kwargs) endpoint = kwargs.pop("endpoint", Endpoint.FEED.value) @@ -1216,17 +1224,25 @@ def nad(self, **kwargs) -> FeedsResults: Apex-level domains (e.g. example.com but not www.example.com) that we observe based on the latest lifecycle of the domain. A domain may be seen either for the first time ever, or again after at least 10 days of inactivity (no observed resolutions in DNS). Populated with our global passive DNS (pDNS) sensor network. - domain: str: Filter for an exact domain or a substring contained within a domain by prefixing or suffixing your substring with "*". Check the documentation for examples + # Session Management Parameters - before: str: Filter for records before the given time value inclusive or time offset relative to now + sessionID: str: A custom string to distinguish between different sessions. Required when using fromBeginning. - after: str: Filter for records after the given time value inclusive or time offset relative to now + after: str: Start of the query window. Either an integer offset relative to now in seconds, or an absolute ISO 8601 UTC datetime (YYYY-MM-DDTHH:MM:SSZ). - headers: bool: Use in combination with Accept: text/csv headers to control if headers are sent or not + before: str: End of the query window (inclusive). Either an integer from -1 to -432000 (seconds before now), or an absolute ISO 8601 UTC datetime (YYYY-MM-DDTHH:MM:SSZ). The query window covers at most the most recent 5 days; a value older than 5 days returns no records. + + fromBeginning: bool: Requires a valid sessionID. When true on the first request of a new session, returns the first hour of data in the time window instead of the last. Using it with an existing sessionID returns HTTP 406; using it without a sessionID or with a non-boolean value returns HTTP 422. + + # Filter Parameters + + domain: str: Filter for an exact domain or a substring contained within a domain by prefixing or suffixing your substring with "*". Check the documentation for examples + + # Result formatting parameters - sessionID: str: A custom string to distinguish between different sessions + headers: bool: Use in combination with Accept: text/csv headers to control if headers are sent or not - top: int: Limit the number of results to the top N, where N is the value of this parameter. + top: int: Limit the number of results to the top N, where N is a positive integer from 1 to 1,000,000,000. """ validate_feeds_parameters(kwargs) endpoint = kwargs.pop("endpoint", Endpoint.FEED.value) @@ -1251,17 +1267,25 @@ def domainrdap(self, **kwargs) -> FeedsResults: Compliments the 5-Minute WHOIS Feed as registries and registrars switch from Whois to RDAP. Contains parsed and raw RDAP-format domain registration data, emitted as soon as they are collected and parsed into a normalized structure. - domain: str: Filter for an exact domain or a substring contained within a domain by prefixing or suffixing your substring with "*". Check the documentation for examples + NOTE: Unlike the other threat feeds, the Parsed Domain RDAP feed exclusively returns JSON and does not support CSV (text/csv). Requesting CSV or the headers parameter returns an HTTP 422 error. - before: str: Filter for records before the given time value inclusive or time offset relative to now + # Session Management Parameters - after: str: Filter for records after the given time value inclusive or time offset relative to now + sessionID: str: A custom string to distinguish between different sessions. Required when using fromBeginning. - headers: bool: Use in combination with Accept: text/csv headers to control if headers are sent or not + after: str: Start of the query window. Either an integer offset relative to now in seconds, or an absolute ISO 8601 UTC datetime (YYYY-MM-DDTHH:MM:SSZ). + + before: str: End of the query window (inclusive). Either an integer from -1 to -432000 (seconds before now), or an absolute ISO 8601 UTC datetime (YYYY-MM-DDTHH:MM:SSZ). The query window covers at most the most recent 5 days; a value older than 5 days returns no records. + + fromBeginning: bool: Requires a valid sessionID. When true on the first request of a new session, returns the first hour of data in the time window instead of the last. Using it with an existing sessionID returns HTTP 406; using it without a sessionID or with a non-boolean value returns HTTP 422. - sessionID: str: A custom string to distinguish between different sessions + # Filter Parameters - top: int: Limit the number of results to the top N, where N is the value of this parameter. + domain: str: Filter for an exact domain or a substring contained within a domain by prefixing or suffixing your substring with "*". Check the documentation for examples + + # Result formatting parameters + + top: int: Limit the number of results to the top N, where N is a positive integer from 1 to 1,000,000,000. """ validate_feeds_parameters(kwargs) endpoint = kwargs.pop("endpoint", Endpoint.FEED.value) @@ -1280,17 +1304,25 @@ def domaindiscovery(self, **kwargs) -> FeedsResults: Contains domains that are newly-discovered by Domain Tools in both passive and active DNS sources, emitted as soon as they are first observed. New domains as they are either discovered in domain registration information, observed by our global sensor network, or reported by trusted third parties. - domain: str: Filter for an exact domain or a substring contained within a domain by prefixing or suffixing your substring with "*". Check the documentation for examples + # Session Management Parameters - before: str: Filter for records before the given time value inclusive or time offset relative to now + sessionID: str: A custom string to distinguish between different sessions. Required when using fromBeginning. - after: str: Filter for records after the given time value inclusive or time offset relative to now + after: str: Start of the query window. Either an integer offset relative to now in seconds, or an absolute ISO 8601 UTC datetime (YYYY-MM-DDTHH:MM:SSZ). - headers: bool: Use in combination with Accept: text/csv headers to control if headers are sent or not + before: str: End of the query window (inclusive). Either an integer from -1 to -432000 (seconds before now), or an absolute ISO 8601 UTC datetime (YYYY-MM-DDTHH:MM:SSZ). The query window covers at most the most recent 5 days; a value older than 5 days returns no records. + + fromBeginning: bool: Requires a valid sessionID. When true on the first request of a new session, returns the first hour of data in the time window instead of the last. Using it with an existing sessionID returns HTTP 406; using it without a sessionID or with a non-boolean value returns HTTP 422. - sessionID: str: A custom string to distinguish between different sessions + # Filter Parameters - top: int: Limit the number of results to the top N, where N is the value of this parameter. + domain: str: Filter for an exact domain or a substring contained within a domain by prefixing or suffixing your substring with "*". Check the documentation for examples + + # Result formatting parameters + + headers: bool: Use in combination with Accept: text/csv headers to control if headers are sent or not + + top: int: Limit the number of results to the top N, where N is a positive integer from 1 to 1,000,000,000. """ validate_feeds_parameters(kwargs) endpoint = kwargs.pop("endpoint", Endpoint.FEED.value) @@ -1315,17 +1347,25 @@ def noh(self, **kwargs) -> FeedsResults: Contains fully qualified domain names (i.e. host names) that have never been seen before in passive DNS, emitted as soon as they are first observed. Hostname resolutions that we observe for the first time with our global DNS sensor network. - domain: str: Filter for an exact domain or a substring contained within a domain by prefixing or suffixing your substring with "*". Check the documentation for examples + # Session Management Parameters - before: str: Filter for records before the given time value inclusive or time offset relative to now + sessionID: str: A custom string to distinguish between different sessions. Required when using fromBeginning. - after: str: Filter for records after the given time value inclusive or time offset relative to now + after: str: Start of the query window. Either an integer offset relative to now in seconds, or an absolute ISO 8601 UTC datetime (YYYY-MM-DDTHH:MM:SSZ). - headers: bool: Use in combination with Accept: text/csv headers to control if headers are sent or not + before: str: End of the query window (inclusive). Either an integer from -1 to -432000 (seconds before now), or an absolute ISO 8601 UTC datetime (YYYY-MM-DDTHH:MM:SSZ). The query window covers at most the most recent 5 days; a value older than 5 days returns no records. - sessionID: str: A custom string to distinguish between different sessions + fromBeginning: bool: Requires a valid sessionID. When true on the first request of a new session, returns the first hour of data in the time window instead of the last. Using it with an existing sessionID returns HTTP 406; using it without a sessionID or with a non-boolean value returns HTTP 422. + + # Filter Parameters + + domain: str: Filter for an exact domain or a substring contained within a domain by prefixing or suffixing your substring with "*". Check the documentation for examples + + # Result formatting parameters + + headers: bool: Use in combination with Accept: text/csv headers to control if headers are sent or not - top: int: Limit the number of results to the top N, where N is the value of this parameter. + top: int: Limit the number of results to the top N, where N is a positive integer from 1 to 1,000,000,000. """ validate_feeds_parameters(kwargs) endpoint = kwargs.pop("endpoint", Endpoint.FEED.value) @@ -1349,17 +1389,35 @@ def realtime_domain_risk(self, **kwargs) -> FeedsResults: """Returns back list of the realtime domain risk feed. Contains realtime domain risk information for apex-level domains, regardless of observed traffic. + # Session Management Parameters + + sessionID: str: A custom string to distinguish between different sessions. Required when using fromBeginning. + + after: str: Start of the query window. Either an integer offset relative to now in seconds, or an absolute ISO 8601 UTC datetime (YYYY-MM-DDTHH:MM:SSZ). + + before: str: End of the query window (inclusive). Either an integer from -1 to -432000 (seconds before now), or an absolute ISO 8601 UTC datetime (YYYY-MM-DDTHH:MM:SSZ). The query window covers at most the most recent 5 days; a value older than 5 days returns no records. + + fromBeginning: bool: Requires a valid sessionID. When true on the first request of a new session, returns the first hour of data in the time window instead of the last. Using it with an existing sessionID returns HTTP 406; using it without a sessionID or with a non-boolean value returns HTTP 422. + + # Filter Parameters + domain: str: Filter for an exact domain or a substring contained within a domain by prefixing or suffixing your substring with "*". Check the documentation for examples - before: str: Filter for records before the given time value inclusive or time offset relative to now + overall_min: int: Minimum overall combined risk score (1 to 99). Optional. When combined with other risk filters, acts as a logical AND (a domain must meet ALL specified thresholds). - after: str: Filter for records after the given time value inclusive or time offset relative to now + malware_min: int: Minimum malware risk score (1 to 99). Optional. Combined with other risk filters as a logical AND. - headers: bool: Use in combination with Accept: text/csv headers to control if headers are sent or not + phishing_min: int: Minimum phishing risk score (1 to 99). Optional. Combined with other risk filters as a logical AND. + + spam_min: int: Minimum spam risk score (1 to 99). Optional. Combined with other risk filters as a logical AND. + + proximity_min: int: Minimum proximity risk score (1 to 99). Optional. Combined with other risk filters as a logical AND. - sessionID: str: A custom string to distinguish between different sessions + # Result formatting parameters - top: int: Limit the number of results to the top N, where N is the value of this parameter. + headers: bool: Use in combination with Accept: text/csv headers to control if headers are sent or not + + top: int: Limit the number of results to the top N, where N is a positive integer from 1 to 1,000,000,000. For risk feeds, results are sorted by all_threats_combined_percent (descending). """ validate_feeds_parameters(kwargs) endpoint = kwargs.pop("endpoint", Endpoint.FEED.value) @@ -1383,17 +1441,35 @@ def domainhotlist(self, **kwargs) -> FeedsResults: """Returns back list of domain hotlist feed. Contains high-risk, apex-level domains that are observed by DomainTools' global sensor network to be active within 24 hours. + # Session Management Parameters + + sessionID: str: A custom string to distinguish between different sessions. Required when using fromBeginning. + + after: str: Start of the query window. Either an integer offset relative to now in seconds, or an absolute ISO 8601 UTC datetime (YYYY-MM-DDTHH:MM:SSZ). + + before: str: End of the query window (inclusive). Either an integer from -1 to -432000 (seconds before now), or an absolute ISO 8601 UTC datetime (YYYY-MM-DDTHH:MM:SSZ). The query window covers at most the most recent 5 days; a value older than 5 days returns no records. + + fromBeginning: bool: Requires a valid sessionID. When true on the first request of a new session, returns the first hour of data in the time window instead of the last. Using it with an existing sessionID returns HTTP 406; using it without a sessionID or with a non-boolean value returns HTTP 422. + + # Filter Parameters + domain: str: Filter for an exact domain or a substring contained within a domain by prefixing or suffixing your substring with "*". Check the documentation for examples - before: str: Filter for records before the given time value inclusive or time offset relative to now + overall_min: int: Minimum overall combined risk score (1 to 99). Optional. When combined with other risk filters, acts as a logical AND (a domain must meet ALL specified thresholds). - after: str: Filter for records after the given time value inclusive or time offset relative to now + malware_min: int: Minimum malware risk score (1 to 99). Optional. Combined with other risk filters as a logical AND. - headers: bool: Use in combination with Accept: text/csv headers to control if headers are sent or not + phishing_min: int: Minimum phishing risk score (1 to 99). Optional. Combined with other risk filters as a logical AND. + + spam_min: int: Minimum spam risk score (1 to 99). Optional. Combined with other risk filters as a logical AND. - sessionID: str: A custom string to distinguish between different sessions + proximity_min: int: Minimum proximity risk score (1 to 99). Optional. Combined with other risk filters as a logical AND. - top: int: Limit the number of results to the top N, where N is the value of this parameter. + # Result formatting parameters + + headers: bool: Use in combination with Accept: text/csv headers to control if headers are sent or not + + top: int: Limit the number of results to the top N, where N is a positive integer from 1 to 1,000,000,000. For risk feeds, results are sorted by all_threats_combined_percent (descending). """ validate_feeds_parameters(kwargs) endpoint = kwargs.pop("endpoint", Endpoint.FEED.value) @@ -1417,17 +1493,53 @@ def iphotlist(self, **kwargs) -> FeedsResults: """Returns back list of ip hotlist feed. Captures IP addresses that meet strict criteria for both risk level and recent activity, making it ideal for immediate blocking and threat response. - before: str: Filter for records before the given time value inclusive or time offset relative to now + # Session Management Parameters - after: str: Filter for records after the given time value inclusive or time offset relative to now + sessionID: str: A custom string to distinguish between different sessions. Required when using fromBeginning. - headers: bool: Use in combination with Accept: text/csv headers to control if headers are sent or not + after: str: Start of the query window. Either an integer offset relative to now in seconds, or an absolute ISO 8601 UTC datetime (YYYY-MM-DDTHH:MM:SSZ). + + before: str: End of the query window (inclusive). Either an integer from -1 to -432000 (seconds before now), or an absolute ISO 8601 UTC datetime (YYYY-MM-DDTHH:MM:SSZ). The query window covers at most the most recent 5 days; a value older than 5 days returns no records. + + fromBeginning: bool: Requires a valid sessionID. When true on the first request of a new session, returns the first hour of data in the time window instead of the last. Using it with an existing sessionID returns HTTP 406; using it without a sessionID or with a non-boolean value returns HTTP 422. + + # Filter Parameters + + pdns_resolutions_min: int: Return only IP addresses that have had at least this many distinct domains actively resolving to them within the last 24 hours (positive integer). + + bad_pdns_resolutions_min: int: Return only IPs with at least this many confirmed bad (malicious) domains actively resolving within the last 24 hours (positive integer). + + total_domains_max: int: Return only IPs hosting no more than this many total domains (positive integer). Useful for filtering out superhosters such as CDNs or large public hosting providers. + + third_party_threats_min: int: Return only IPs with at least this many domains independently confirmed as threats on external, third-party intelligence feeds (positive integer). + + all_threats_combined_percent_min: int: Return only IPs where at least this percentage (0-100) of total hosted domains are confirmed or predicted malicious across all threat types. + + combined_phishing_percent_min: int: Return only IPs where at least this percentage (0-100) of total domains are confirmed or predicted as phishing. + + combined_malware_percent_min: int: Return only IPs where at least this percentage (0-100) of total domains are confirmed or predicted as malware. + + combined_spam_percent_min: int: Return only IPs where at least this percentage (0-100) of total domains are confirmed or predicted as spam. + + all_threats_percent_min: int: Return only IPs where at least this percentage (0-100) of total domains are actively confirmed with threats across all threat types. + + percent_phishing_min: int: Return only IPs where at least this percentage (0-100) of total domains are actively confirmed as phishing. + + percent_malware_min: int: Return only IPs where at least this percentage (0-100) of total domains are actively confirmed as malware. + + percent_spam_min: int: Return only IPs where at least this percentage (0-100) of total domains are actively confirmed as spam. - sessionID: str: A custom string to distinguish between different sessions + asn: int: Restrict output to IPs belonging to a specific Autonomous System Number (digits only, e.g. 15169). No AS prefix and wildcards are not supported. - fromBeginning: bool: Requires a sessionID. When used with a new session ID, returns the first hour of data in the time window (rather than the last). Returns an error if the session ID already exists + organization: str: Filter for IPs associated with a specific organization by its full exact name (e.g. Example Hosting Inc). Matches the exact string only; wildcards are not supported. - top: int: Limits the number of results in the response payload. Primarily intended for testing. When you apply this parameter to risk feeds, results are sorted by all_threats_combined_percent (descending). + country_code: str: Filter results to IPs geolocated to a specific case-sensitive two-letter country code (e.g. CN, US, NL). + + # Result formatting parameters + + headers: bool: Use in combination with Accept: text/csv headers to control if headers are sent or not + + top: int: Limits the number of results in the response payload (a positive integer from 1 to 1,000,000,000). Primarily intended for testing. When you apply this parameter to risk feeds, results are sorted by all_threats_combined_percent (descending). """ validate_feeds_parameters(kwargs) endpoint = kwargs.pop("endpoint", Endpoint.FEED.value) @@ -1451,17 +1563,53 @@ def iprisk(self, **kwargs) -> FeedsResults: """Returns back list of domain hotlist feed. Captures all IP addresses that actively host one or more domains, providing risk assessment and enrichment data for each IP address. - before: str: Filter for records before the given time value inclusive or time offset relative to now + # Session Management Parameters - after: str: Filter for records after the given time value inclusive or time offset relative to now + sessionID: str: A custom string to distinguish between different sessions. Required when using fromBeginning. - headers: bool: Use in combination with Accept: text/csv headers to control if headers are sent or not + after: str: Start of the query window. Either an integer offset relative to now in seconds, or an absolute ISO 8601 UTC datetime (YYYY-MM-DDTHH:MM:SSZ). + + before: str: End of the query window (inclusive). Either an integer from -1 to -432000 (seconds before now), or an absolute ISO 8601 UTC datetime (YYYY-MM-DDTHH:MM:SSZ). The query window covers at most the most recent 5 days; a value older than 5 days returns no records. + + fromBeginning: bool: Requires a valid sessionID. When true on the first request of a new session, returns the first hour of data in the time window instead of the last. Using it with an existing sessionID returns HTTP 406; using it without a sessionID or with a non-boolean value returns HTTP 422. + + # Filter Parameters + + pdns_resolutions_min: int: Return only IP addresses that have had at least this many distinct domains actively resolving to them within the last 24 hours (positive integer). + + bad_pdns_resolutions_min: int: Return only IPs with at least this many confirmed bad (malicious) domains actively resolving within the last 24 hours (positive integer). + + total_domains_max: int: Return only IPs hosting no more than this many total domains (positive integer). Useful for filtering out superhosters such as CDNs or large public hosting providers. + + third_party_threats_min: int: Return only IPs with at least this many domains independently confirmed as threats on external, third-party intelligence feeds (positive integer). + + all_threats_combined_percent_min: int: Return only IPs where at least this percentage (0-100) of total hosted domains are confirmed or predicted malicious across all threat types. - sessionID: str: A custom string to distinguish between different sessions + combined_phishing_percent_min: int: Return only IPs where at least this percentage (0-100) of total domains are confirmed or predicted as phishing. + + combined_malware_percent_min: int: Return only IPs where at least this percentage (0-100) of total domains are confirmed or predicted as malware. + + combined_spam_percent_min: int: Return only IPs where at least this percentage (0-100) of total domains are confirmed or predicted as spam. + + all_threats_percent_min: int: Return only IPs where at least this percentage (0-100) of total domains are actively confirmed with threats across all threat types. + + percent_phishing_min: int: Return only IPs where at least this percentage (0-100) of total domains are actively confirmed as phishing. + + percent_malware_min: int: Return only IPs where at least this percentage (0-100) of total domains are actively confirmed as malware. + + percent_spam_min: int: Return only IPs where at least this percentage (0-100) of total domains are actively confirmed as spam. + + asn: int: Restrict output to IPs belonging to a specific Autonomous System Number (digits only, e.g. 15169). No AS prefix and wildcards are not supported. + + organization: str: Filter for IPs associated with a specific organization by its full exact name (e.g. Example Hosting Inc). Matches the exact string only; wildcards are not supported. + + country_code: str: Filter results to IPs geolocated to a specific case-sensitive two-letter country code (e.g. CN, US, NL). + + # Result formatting parameters + + headers: bool: Use in combination with Accept: text/csv headers to control if headers are sent or not - fromBeginning: bool: Requires a sessionID. When used with a new session ID, returns the first hour of data in the time window (rather than the last). Returns an error if the session ID already exists - - top: int: Limits the number of results in the response payload. Primarily intended for testing. When you apply this parameter to risk feeds, results are sorted by all_threats_combined_percent (descending). + top: int: Limits the number of results in the response payload (a positive integer from 1 to 1,000,000,000). Primarily intended for testing. When you apply this parameter to risk feeds, results are sorted by all_threats_combined_percent (descending). """ validate_feeds_parameters(kwargs) endpoint = kwargs.pop("endpoint", Endpoint.FEED.value) diff --git a/domaintools/cli/commands/feeds.py b/domaintools/cli/commands/feeds.py index 4f1087c..432bea9 100644 --- a/domaintools/cli/commands/feeds.py +++ b/domaintools/cli/commands/feeds.py @@ -37,13 +37,6 @@ def feeds_nad( "--no-header-auth", help="Don't use header authentication", ), - output_format: str = typer.Option( - "jsonl", - "-f", - "--format", - help=f"Output format in [{OutputFormat.JSONL.value}, {OutputFormat.CSV.value}]", - callback=DTCLICommand.validate_feeds_format_input, - ), endpoint: str = typer.Option( Endpoint.FEED.value, "-e", @@ -51,10 +44,11 @@ def feeds_nad( help=f"Valid endpoints: [{Endpoint.FEED.value}, {Endpoint.DOWNLOAD.value}]", callback=DTCLICommand.validate_endpoint_input, ), + # Session Management Parameters sessionID: str = typer.Option( None, "--session-id", - help="Unique identifier for the session", + help="Unique identifier for the session. Required when using --frombeginning", ), after: str = typer.Option( None, @@ -65,15 +59,30 @@ def feeds_nad( before: str = typer.Option( None, "--before", - help="The end of the query window in seconds, relative to the current time, inclusive", + help="End of the query window (inclusive). Integer from -1 to -432000 (seconds before now) or an absolute ISO 8601 UTC datetime. The window covers at most the most recent 5 days", callback=DTCLICommand.validate_after_or_before_input, ), + fromBeginning: bool = typer.Option( + None, + "-fb", + "--frombeginning", + help="Requires a sessionID. When used with a new session ID, returns the first hour of data in the time window (rather than the last). Returns an error if the session ID already exists", + ), + # Filter Parameters domain: str = typer.Option( None, "-d", "--domain", help="A string value used to filter feed results", ), + # Result formatting parameters + output_format: str = typer.Option( + "jsonl", + "-f", + "--format", + help=f"Output format in [{OutputFormat.JSONL.value}, {OutputFormat.CSV.value}]", + callback=DTCLICommand.validate_feeds_format_input, + ), headers: bool = typer.Option( False, "--headers", @@ -117,13 +126,6 @@ def feeds_nod( "--no-header-auth", help="Don't use header authentication", ), - output_format: str = typer.Option( - "jsonl", - "-f", - "--format", - help=f"Output format in [{OutputFormat.JSONL.value}, {OutputFormat.CSV.value}]", - callback=DTCLICommand.validate_feeds_format_input, - ), endpoint: str = typer.Option( Endpoint.FEED.value, "-e", @@ -131,10 +133,11 @@ def feeds_nod( help=f"Valid endpoints: [{Endpoint.FEED.value}, {Endpoint.DOWNLOAD.value}]", callback=DTCLICommand.validate_endpoint_input, ), + # Session Management Parameters sessionID: str = typer.Option( None, "--session-id", - help="Unique identifier for the session", + help="Unique identifier for the session. Required when using --frombeginning", ), after: str = typer.Option( None, @@ -145,15 +148,30 @@ def feeds_nod( before: str = typer.Option( None, "--before", - help="The end of the query window in seconds, relative to the current time, inclusive", + help="End of the query window (inclusive). Integer from -1 to -432000 (seconds before now) or an absolute ISO 8601 UTC datetime. The window covers at most the most recent 5 days", callback=DTCLICommand.validate_after_or_before_input, ), + fromBeginning: bool = typer.Option( + None, + "-fb", + "--frombeginning", + help="Requires a sessionID. When used with a new session ID, returns the first hour of data in the time window (rather than the last). Returns an error if the session ID already exists", + ), + # Filter Parameters domain: str = typer.Option( None, "-d", "--domain", help="A string value used to filter feed results", ), + # Result formatting parameters + output_format: str = typer.Option( + "jsonl", + "-f", + "--format", + help=f"Output format in [{OutputFormat.JSONL.value}, {OutputFormat.CSV.value}]", + callback=DTCLICommand.validate_feeds_format_input, + ), headers: bool = typer.Option( False, "--headers", @@ -204,10 +222,11 @@ def feeds_domainrdap( help=f"Valid endpoints: [{Endpoint.FEED.value}, {Endpoint.DOWNLOAD.value}]", callback=DTCLICommand.validate_endpoint_input, ), + # Session Management Parameters sessionID: str = typer.Option( None, "--session-id", - help="Unique identifier for the session", + help="Unique identifier for the session. Required when using --frombeginning", ), after: str = typer.Option( None, @@ -218,15 +237,24 @@ def feeds_domainrdap( before: str = typer.Option( None, "--before", - help="The end of the query window in seconds, relative to the current time, inclusive", + help="End of the query window (inclusive). Integer from -1 to -432000 (seconds before now) or an absolute ISO 8601 UTC datetime. The window covers at most the most recent 5 days", callback=DTCLICommand.validate_after_or_before_input, ), + fromBeginning: bool = typer.Option( + None, + "-fb", + "--frombeginning", + help="Requires a sessionID. When used with a new session ID, returns the first hour of data in the time window (rather than the last). Returns an error if the session ID already exists", + ), + # Filter Parameters domain: str = typer.Option( None, "-d", "--domain", help="A string value used to filter feed results", ), + # Result formatting parameters + # Note: the Parsed Domain RDAP feed returns JSON only; CSV format and headers are not supported. top: int = typer.Option( None, "--top", @@ -265,13 +293,6 @@ def feeds_domaindiscovery( "--no-header-auth", help="Don't use header authentication", ), - output_format: str = typer.Option( - "jsonl", - "-f", - "--format", - help=f"Output format in [{OutputFormat.JSONL.value}, {OutputFormat.CSV.value}]", - callback=DTCLICommand.validate_feeds_format_input, - ), endpoint: str = typer.Option( Endpoint.FEED.value, "-e", @@ -279,10 +300,11 @@ def feeds_domaindiscovery( help=f"Valid endpoints: [{Endpoint.FEED.value}, {Endpoint.DOWNLOAD.value}]", callback=DTCLICommand.validate_endpoint_input, ), + # Session Management Parameters sessionID: str = typer.Option( None, "--session-id", - help="Unique identifier for the session", + help="Unique identifier for the session. Required when using --frombeginning", ), after: str = typer.Option( None, @@ -293,15 +315,30 @@ def feeds_domaindiscovery( before: str = typer.Option( None, "--before", - help="The end of the query window in seconds, relative to the current time, inclusive", + help="End of the query window (inclusive). Integer from -1 to -432000 (seconds before now) or an absolute ISO 8601 UTC datetime. The window covers at most the most recent 5 days", callback=DTCLICommand.validate_after_or_before_input, ), + fromBeginning: bool = typer.Option( + None, + "-fb", + "--frombeginning", + help="Requires a sessionID. When used with a new session ID, returns the first hour of data in the time window (rather than the last). Returns an error if the session ID already exists", + ), + # Filter Parameters domain: str = typer.Option( None, "-d", "--domain", help="A string value used to filter feed results", ), + # Result formatting parameters + output_format: str = typer.Option( + "jsonl", + "-f", + "--format", + help=f"Output format in [{OutputFormat.JSONL.value}, {OutputFormat.CSV.value}]", + callback=DTCLICommand.validate_feeds_format_input, + ), headers: bool = typer.Option( False, "--headers", @@ -345,13 +382,6 @@ def feeds_noh( "--no-header-auth", help="Don't use header authentication", ), - output_format: str = typer.Option( - "jsonl", - "-f", - "--format", - help=f"Output format in [{OutputFormat.JSONL.value}, {OutputFormat.CSV.value}]", - callback=DTCLICommand.validate_feeds_format_input, - ), endpoint: str = typer.Option( Endpoint.FEED.value, "-e", @@ -359,10 +389,11 @@ def feeds_noh( help=f"Valid endpoints: [{Endpoint.FEED.value}, {Endpoint.DOWNLOAD.value}]", callback=DTCLICommand.validate_endpoint_input, ), + # Session Management Parameters sessionID: str = typer.Option( None, "--session-id", - help="Unique identifier for the session", + help="Unique identifier for the session. Required when using --frombeginning", ), after: str = typer.Option( None, @@ -373,15 +404,30 @@ def feeds_noh( before: str = typer.Option( None, "--before", - help="The end of the query window in seconds, relative to the current time, inclusive", + help="End of the query window (inclusive). Integer from -1 to -432000 (seconds before now) or an absolute ISO 8601 UTC datetime. The window covers at most the most recent 5 days", callback=DTCLICommand.validate_after_or_before_input, ), + fromBeginning: bool = typer.Option( + None, + "-fb", + "--frombeginning", + help="Requires a sessionID. When used with a new session ID, returns the first hour of data in the time window (rather than the last). Returns an error if the session ID already exists", + ), + # Filter Parameters domain: str = typer.Option( None, "-d", "--domain", help="A string value used to filter feed results", ), + # Result formatting parameters + output_format: str = typer.Option( + "jsonl", + "-f", + "--format", + help=f"Output format in [{OutputFormat.JSONL.value}, {OutputFormat.CSV.value}]", + callback=DTCLICommand.validate_feeds_format_input, + ), headers: bool = typer.Option( False, "--headers", @@ -425,13 +471,6 @@ def feeds_domainhotlist( "--no-header-auth", help="Don't use header authentication", ), - output_format: str = typer.Option( - "jsonl", - "-f", - "--format", - help=f"Output format in [{OutputFormat.JSONL.value}, {OutputFormat.CSV.value}]", - callback=DTCLICommand.validate_feeds_format_input, - ), endpoint: str = typer.Option( Endpoint.FEED.value, "-e", @@ -439,10 +478,11 @@ def feeds_domainhotlist( help=f"Valid endpoints: [{Endpoint.FEED.value}, {Endpoint.DOWNLOAD.value}]", callback=DTCLICommand.validate_endpoint_input, ), + # Session Management Parameters sessionID: str = typer.Option( None, "--session-id", - help="Unique identifier for the session", + help="Unique identifier for the session. Required when using --frombeginning", ), after: str = typer.Option( None, @@ -453,15 +493,55 @@ def feeds_domainhotlist( before: str = typer.Option( None, "--before", - help="The end of the query window in seconds, relative to the current time, inclusive", + help="End of the query window (inclusive). Integer from -1 to -432000 (seconds before now) or an absolute ISO 8601 UTC datetime. The window covers at most the most recent 5 days", callback=DTCLICommand.validate_after_or_before_input, ), + fromBeginning: bool = typer.Option( + None, + "-fb", + "--frombeginning", + help="Requires a sessionID. When used with a new session ID, returns the first hour of data in the time window (rather than the last). Returns an error if the session ID already exists", + ), + # Filter Parameters domain: str = typer.Option( None, "-d", "--domain", help="A string value used to filter feed results", ), + overall_min: int = typer.Option( + None, + "--overall-min", + help="Minimum overall combined risk score (1-99). Combined with other risk filters as a logical AND", + ), + malware_min: int = typer.Option( + None, + "--malware-min", + help="Minimum malware risk score (1-99). Combined with other risk filters as a logical AND", + ), + phishing_min: int = typer.Option( + None, + "--phishing-min", + help="Minimum phishing risk score (1-99). Combined with other risk filters as a logical AND", + ), + spam_min: int = typer.Option( + None, + "--spam-min", + help="Minimum spam risk score (1-99). Combined with other risk filters as a logical AND", + ), + proximity_min: int = typer.Option( + None, + "--proximity-min", + help="Minimum proximity risk score (1-99). Combined with other risk filters as a logical AND", + ), + # Result formatting parameters + output_format: str = typer.Option( + "jsonl", + "-f", + "--format", + help=f"Output format in [{OutputFormat.JSONL.value}, {OutputFormat.CSV.value}]", + callback=DTCLICommand.validate_feeds_format_input, + ), headers: bool = typer.Option( False, "--headers", @@ -470,7 +550,7 @@ def feeds_domainhotlist( top: int = typer.Option( None, "--top", - help="Number of results to return in the response payload. This is ignored in download endpoint", + help="Number of results to return in the response payload. This is ignored in download endpoint. For risk feeds, results are sorted by all_threats_combined_percent (descending)", ), ): DTCLICommand.run(name=c.FEEDS_DOMAINHOTLIST, params=ctx.params) @@ -505,13 +585,6 @@ def feeds_realtime_domain_risk( "--no-header-auth", help="Don't use header authentication", ), - output_format: str = typer.Option( - "jsonl", - "-f", - "--format", - help=f"Output format in [{OutputFormat.JSONL.value}, {OutputFormat.CSV.value}]", - callback=DTCLICommand.validate_feeds_format_input, - ), endpoint: str = typer.Option( Endpoint.FEED.value, "-e", @@ -519,10 +592,11 @@ def feeds_realtime_domain_risk( help=f"Valid endpoints: [{Endpoint.FEED.value}, {Endpoint.DOWNLOAD.value}]", callback=DTCLICommand.validate_endpoint_input, ), + # Session Management Parameters sessionID: str = typer.Option( None, "--session-id", - help="Unique identifier for the session", + help="Unique identifier for the session. Required when using --frombeginning", ), after: str = typer.Option( None, @@ -533,15 +607,55 @@ def feeds_realtime_domain_risk( before: str = typer.Option( None, "--before", - help="The end of the query window in seconds, relative to the current time, inclusive", + help="End of the query window (inclusive). Integer from -1 to -432000 (seconds before now) or an absolute ISO 8601 UTC datetime. The window covers at most the most recent 5 days", callback=DTCLICommand.validate_after_or_before_input, ), + fromBeginning: bool = typer.Option( + None, + "-fb", + "--frombeginning", + help="Requires a sessionID. When used with a new session ID, returns the first hour of data in the time window (rather than the last). Returns an error if the session ID already exists", + ), + # Filter Parameters domain: str = typer.Option( None, "-d", "--domain", help="A string value used to filter feed results", ), + overall_min: int = typer.Option( + None, + "--overall-min", + help="Minimum overall combined risk score (1-99). Combined with other risk filters as a logical AND", + ), + malware_min: int = typer.Option( + None, + "--malware-min", + help="Minimum malware risk score (1-99). Combined with other risk filters as a logical AND", + ), + phishing_min: int = typer.Option( + None, + "--phishing-min", + help="Minimum phishing risk score (1-99). Combined with other risk filters as a logical AND", + ), + spam_min: int = typer.Option( + None, + "--spam-min", + help="Minimum spam risk score (1-99). Combined with other risk filters as a logical AND", + ), + proximity_min: int = typer.Option( + None, + "--proximity-min", + help="Minimum proximity risk score (1-99). Combined with other risk filters as a logical AND", + ), + # Result formatting parameters + output_format: str = typer.Option( + "jsonl", + "-f", + "--format", + help=f"Output format in [{OutputFormat.JSONL.value}, {OutputFormat.CSV.value}]", + callback=DTCLICommand.validate_feeds_format_input, + ), headers: bool = typer.Option( False, "--headers", @@ -550,7 +664,7 @@ def feeds_realtime_domain_risk( top: int = typer.Option( None, "--top", - help="Number of results to return in the response payload. This is ignored in download endpoint", + help="Number of results to return in the response payload. This is ignored in download endpoint. For risk feeds, results are sorted by all_threats_combined_percent (descending)", ), ): DTCLICommand.run(name=c.FEEDS_REALTIME_DOMAIN_RISK, params=ctx.params) @@ -585,13 +699,6 @@ def feeds_iphotlist( "--no-header-auth", help="Don't use header authentication", ), - output_format: str = typer.Option( - "jsonl", - "-f", - "--format", - help=f"Output format in [{OutputFormat.JSONL.value}, {OutputFormat.CSV.value}]", - callback=DTCLICommand.validate_feeds_format_input, - ), endpoint: str = typer.Option( Endpoint.FEED.value, "-e", @@ -599,10 +706,11 @@ def feeds_iphotlist( help=f"Valid endpoints: [{Endpoint.FEED.value}, {Endpoint.DOWNLOAD.value}]", callback=DTCLICommand.validate_endpoint_input, ), + # Session Management Parameters sessionID: str = typer.Option( None, "--session-id", - help="Unique identifier for the session", + help="Unique identifier for the session. Required when using --frombeginning", ), after: str = typer.Option( None, @@ -613,7 +721,7 @@ def feeds_iphotlist( before: str = typer.Option( None, "--before", - help="The end of the query window in seconds, relative to the current time, inclusive", + help="End of the query window (inclusive). Integer from -1 to -432000 (seconds before now) or an absolute ISO 8601 UTC datetime. The window covers at most the most recent 5 days", callback=DTCLICommand.validate_after_or_before_input, ), fromBeginning: bool = typer.Option( @@ -622,16 +730,100 @@ def feeds_iphotlist( "--frombeginning", help="Requires a sessionID. When used with a new session ID, returns the first hour of data in the time window (rather than the last). Returns an error if the session ID already exists", ), - top: int = typer.Option( + # Filter Parameters + pdns_resolutions_min: int = typer.Option( None, - "--top", - help="Number of results to return in the response payload. This is ignored in download endpoint", + "--pdns-resolutions-min", + help="Minimum number of distinct domains actively resolving to the IP within the last 24 hours (positive integer)", + ), + bad_pdns_resolutions_min: int = typer.Option( + None, + "--bad-pdns-resolutions-min", + help="Minimum number of confirmed bad (malicious) domains actively resolving to the IP within the last 24 hours (positive integer)", + ), + total_domains_max: int = typer.Option( + None, + "--total-domains-max", + help="Maximum number of total domains hosted on the IP (positive integer). Useful for filtering out superhosters such as CDNs or large hosting providers", + ), + third_party_threats_min: int = typer.Option( + None, + "--third-party-threats-min", + help="Minimum number of hosted domains independently confirmed as threats on external third-party intelligence feeds (positive integer)", + ), + all_threats_combined_percent_min: int = typer.Option( + None, + "--all-threats-combined-percent-min", + help="Minimum percentage (0-100) of hosted domains confirmed or predicted as malicious across all threat types", + ), + combined_phishing_percent_min: int = typer.Option( + None, + "--combined-phishing-percent-min", + help="Minimum percentage (0-100) of hosted domains confirmed or predicted as phishing", + ), + combined_malware_percent_min: int = typer.Option( + None, + "--combined-malware-percent-min", + help="Minimum percentage (0-100) of hosted domains confirmed or predicted as malware", + ), + combined_spam_percent_min: int = typer.Option( + None, + "--combined-spam-percent-min", + help="Minimum percentage (0-100) of hosted domains confirmed or predicted as spam", + ), + all_threats_percent_min: int = typer.Option( + None, + "--all-threats-percent-min", + help="Minimum percentage (0-100) of hosted domains actively confirmed with threats across all threat types", + ), + percent_phishing_min: int = typer.Option( + None, + "--percent-phishing-min", + help="Minimum percentage (0-100) of hosted domains actively confirmed as phishing", + ), + percent_malware_min: int = typer.Option( + None, + "--percent-malware-min", + help="Minimum percentage (0-100) of hosted domains actively confirmed as malware", + ), + percent_spam_min: int = typer.Option( + None, + "--percent-spam-min", + help="Minimum percentage (0-100) of hosted domains actively confirmed as spam", + ), + asn: int = typer.Option( + None, + "--asn", + help="Autonomous System Number (digits only, e.g. 15169). Restricts output to IPs belonging to a specific routing provider/network. No AS prefix and wildcards are not supported", + ), + organization: str = typer.Option( + None, + "--organization", + help="Full exact name of the organization (e.g. Example Hosting Inc). Matches the exact string only; wildcards are not supported", + ), + country_code: str = typer.Option( + None, + "--country-code", + help="Case-sensitive two-letter country code (e.g. CN, US, NL). Filters results to IPs geolocated to that country", + ), + # Result formatting parameters + output_format: str = typer.Option( + "jsonl", + "-f", + "--format", + help=f"Output format in [{OutputFormat.JSONL.value}, {OutputFormat.CSV.value}]", + callback=DTCLICommand.validate_feeds_format_input, ), headers: bool = typer.Option( False, "--headers", help="Adds a header to the first line of response when text/csv is set in header parameters", ), + top: int = typer.Option( + None, + "--top", + help="Number of results to return in the response payload. This is ignored in download endpoint. For risk feeds, results are sorted by all_threats_combined_percent (descending)", + ), ): DTCLICommand.run(name=c.FEEDS_IPHOTLIST, params=ctx.params) @@ -665,13 +857,6 @@ def feeds_iprisk( "--no-header-auth", help="Don't use header authentication", ), - output_format: str = typer.Option( - "jsonl", - "-f", - "--format", - help=f"Output format in [{OutputFormat.JSONL.value}, {OutputFormat.CSV.value}]", - callback=DTCLICommand.validate_feeds_format_input, - ), endpoint: str = typer.Option( Endpoint.FEED.value, "-e", @@ -679,10 +864,11 @@ def feeds_iprisk( help=f"Valid endpoints: [{Endpoint.FEED.value}, {Endpoint.DOWNLOAD.value}]", callback=DTCLICommand.validate_endpoint_input, ), + # Session Management Parameters sessionID: str = typer.Option( None, "--session-id", - help="Unique identifier for the session", + help="Unique identifier for the session. Required when using --frombeginning", ), after: str = typer.Option( None, @@ -693,7 +879,7 @@ def feeds_iprisk( before: str = typer.Option( None, "--before", - help="The end of the query window in seconds, relative to the current time, inclusive", + help="End of the query window (inclusive). Integer from -1 to -432000 (seconds before now) or an absolute ISO 8601 UTC datetime. The window covers at most the most recent 5 days", callback=DTCLICommand.validate_after_or_before_input, ), fromBeginning: bool = typer.Option( @@ -702,15 +888,99 @@ def feeds_iprisk( "--frombeginning", help="Requires a sessionID. When used with a new session ID, returns the first hour of data in the time window (rather than the last). Returns an error if the session ID already exists", ), - top: int = typer.Option( + # Filter Parameters + pdns_resolutions_min: int = typer.Option( None, - "--top", - help="Number of results to return in the response payload. This is ignored in download endpoint", + "--pdns-resolutions-min", + help="Minimum number of distinct domains actively resolving to the IP within the last 24 hours (positive integer)", + ), + bad_pdns_resolutions_min: int = typer.Option( + None, + "--bad-pdns-resolutions-min", + help="Minimum number of confirmed bad (malicious) domains actively resolving to the IP within the last 24 hours (positive integer)", + ), + total_domains_max: int = typer.Option( + None, + "--total-domains-max", + help="Maximum number of total domains hosted on the IP (positive integer). Useful for filtering out superhosters such as CDNs or large hosting providers", + ), + third_party_threats_min: int = typer.Option( + None, + "--third-party-threats-min", + help="Minimum number of hosted domains independently confirmed as threats on external third-party intelligence feeds (positive integer)", + ), + all_threats_combined_percent_min: int = typer.Option( + None, + "--all-threats-combined-percent-min", + help="Minimum percentage (0-100) of hosted domains confirmed or predicted as malicious across all threat types", + ), + combined_phishing_percent_min: int = typer.Option( + None, + "--combined-phishing-percent-min", + help="Minimum percentage (0-100) of hosted domains confirmed or predicted as phishing", + ), + combined_malware_percent_min: int = typer.Option( + None, + "--combined-malware-percent-min", + help="Minimum percentage (0-100) of hosted domains confirmed or predicted as malware", + ), + combined_spam_percent_min: int = typer.Option( + None, + "--combined-spam-percent-min", + help="Minimum percentage (0-100) of hosted domains confirmed or predicted as spam", + ), + all_threats_percent_min: int = typer.Option( + None, + "--all-threats-percent-min", + help="Minimum percentage (0-100) of hosted domains actively confirmed with threats across all threat types", + ), + percent_phishing_min: int = typer.Option( + None, + "--percent-phishing-min", + help="Minimum percentage (0-100) of hosted domains actively confirmed as phishing", + ), + percent_malware_min: int = typer.Option( + None, + "--percent-malware-min", + help="Minimum percentage (0-100) of hosted domains actively confirmed as malware", + ), + percent_spam_min: int = typer.Option( + None, + "--percent-spam-min", + help="Minimum percentage (0-100) of hosted domains actively confirmed as spam", + ), + asn: int = typer.Option( + None, + "--asn", + help="Autonomous System Number (digits only, e.g. 15169). Restricts output to IPs belonging to a specific routing provider/network. No AS prefix and wildcards are not supported", + ), + organization: str = typer.Option( + None, + "--organization", + help="Full exact name of the organization (e.g. Example Hosting Inc). Matches the exact string only; wildcards are not supported", + ), + country_code: str = typer.Option( + None, + "--country-code", + help="Case-sensitive two-letter country code (e.g. CN, US, NL). Filters results to IPs geolocated to that country", + ), + # Result formatting parameters + output_format: str = typer.Option( + "jsonl", + "-f", + "--format", + help=f"Output format in [{OutputFormat.JSONL.value}, {OutputFormat.CSV.value}]", + callback=DTCLICommand.validate_feeds_format_input, ), headers: bool = typer.Option( False, "--headers", help="Adds a header to the first line of response when text/csv is set in header parameters", ), + top: int = typer.Option( + None, + "--top", + help="Number of results to return in the response payload. This is ignored in download endpoint. For risk feeds, results are sorted by all_threats_combined_percent (descending)", + ), ): DTCLICommand.run(name=c.FEEDS_IPRISK, params=ctx.params) \ No newline at end of file