diff --git a/.github/wordlist.txt b/.github/wordlist.txt index 9638e59db..e411f0c5e 100644 --- a/.github/wordlist.txt +++ b/.github/wordlist.txt @@ -1379,3 +1379,5 @@ WorkflowTriggersCombined Destom ValueError QueryCasesIdsByFilter +SDKDEMO + diff --git a/AUTHORS.md b/AUTHORS.md index cab8e04e8..84a4e73f6 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -94,6 +94,9 @@ This has been a critical element in the development of the FalconPy project. + `@PeroSoy` + Shubham, `@i-shubham01` + Don "Swanson" I., `@Don-Swanson-Adobe` ++ Nick, `nickforsythbarr` ++ `nesies` ++ `David-M-Berry` ## Sponsors diff --git a/samples/README.md b/samples/README.md index 760a51546..6b2379689 100644 --- a/samples/README.md +++ b/samples/README.md @@ -45,7 +45,7 @@ The following samples are categorized by CrowdStrike product, and further catego | Topic | Samples | | :-- | :-- | -| [Hosts](#hosts-samples)
[Host Groups](#hosts-samples)
| List sensors by hostname
Manage duplicate sensors
CUSSED (Manage stale sensors)
Default Groups
Get Host Groups
Hosts Report
Host Search
Host Tagger
Policy Check
RFM Report
Serial Search
Match usernames to hosts
Offset vs. Token
Prune Hosts by Hostname or AID
Quarantine a host
Quarantine a host (updated version) | +| [Hosts](#hosts-samples)
[Host Groups](#hosts-samples)
| List sensors by hostname
Manage duplicate sensors
CUSSED (Manage stale sensors)
Default Groups
Get Host Groups
Hosts Report
Host Search
Host Search Advanced
Host Tagger
Policy Check
RFM Report
Serial Search
Match usernames to hosts
Offset vs. Token
Prune Hosts by Hostname or AID
Quarantine a host
Quarantine a host (updated version) | | [Report Executions](#report-executions-samples) | Retrieve all report results | | [Sensor Download](#sensor-download-samples) | Download the CrowdStrike sensor | | [Sensor Update Policies](#sensor-update-policies-samples) | Clone Update Policy
Create Host Group and attach Update Policy
Policy Wonk | @@ -196,6 +196,7 @@ The samples collected in this section demonstrate leveraging CrowdStrike's Hosts - [Get Host Groups](#get-host-group) - [Hosts Report](#hosts-report) - [Host Search](#host-search) +- [Host Search Advanced](#host-search-advanced) - [Host Tagger](#host-tagger) - [Match usernames to hosts](#match-usernames-to-hosts) - [Offset vs. Token](#offset-vs-token) @@ -332,6 +333,22 @@ This sample demonstrates the following CrowdStrike Hosts API operations: --- +#### Hosts Search Advanced +This [example](hosts#hosts-search-advanced) will demonstrate how to search for host details by hostname. + +[![Hosts](https://img.shields.io/badge/Service%20Class-Hosts_Search-silver?style=for-the-badge&labelColor=C30A16&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAAOCAYAAAAi2ky3AAABhWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw1AUhU9TpaIVBzuIOGSoDmJBVEQ3rUIRKoRaoVUHk5f+CE0akhQXR8G14ODPYtXBxVlXB1dBEPwBcXNzUnSREu9LCi1ifPB4H+e9c7jvXkColZhmtY0Cmm6bqURczGRXxNAruhAEMI1hmVnGrCQl4bu+7hHg512MZ/m/+3N1qzmLAQGReIYZpk28Tjy5aRuc94kjrCirxOfEIyYVSPzIdcXjN84FlwWeGTHTqTniCLFYaGGlhVnR1IgniKOqplO+kPFY5bzFWStVWKNO/sNwTl9e4jrtASSwgEVIEKGggg2UYCNGp06KhRTdx338/a5fIpdCrg0wcsyjDA2y6wefwe/eWvnxMS8pHAfaXxznYxAI7QL1quN8HztO/QQIPgNXetNfrgFTn6RXm1r0COjZBi6um5qyB1zuAH1PhmzKrsTnL+TzwPsZjSkL9N4Cnate3xr3OH0A0tSr5A1wcAgMFSh7zeffHa19+/dNo38/hq9yr+iELI0AAAAGYktHRAAAAAAAAPlDu38AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflDAsTByz7Va2cAAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aCBHSU1QV4EOFwAAAYBJREFUKM+lkjFIlVEYht/zn3sFkYYUyUnIRcemhCtCU6JQOLiIU+QeJEQg6BBIm0s4RBCBLjq5OEvgJC1uOniJhivesLx17/97/vO9b4NK4g25157hfHCGB773/cA0HZIEAKiMj+LWiOxljG/i96pnCFP58XHnrWX2+9cj0dYl9Yu2FE9/9rXrcAAgs2eSyiBfOe/XRD503h/CuffOubQVUXL+Jh9BllzBbyJJBgDclVkO4Kukd8zzkXJbeUljIldFTstsmSHM6S81ma2KfPKlFdkGAMY4wzx/bbXapMy21My+YizdKNq5mDzLkrxafSxySFKjSWX2oTmjKzz4vN0r2lOFcL/Q3V0/mX95ILMXTTGYVfaut/aP2+oCMAvnZgCcsF5fcR0dg65YHAdwB+QApADvu0AuOe/ftlJAD7Nsgmm6yBjDtfWORJZlNtFyo/lR5Z7MyheKA5ktSur7sTAHazSG27pehjAiaVfkN8b4XFIJ/wOzbOx07VNRUuHy7w98CzCcGPyWywAAAABJRU5ErkJggg==)](hosts#hosts-search-advanced) +[![Community Contribution](https://img.shields.io/badge/-Contribution-2C6B07?style=for-the-badge&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAAOCAYAAAAi2ky3AAABhWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw1AUhU9TpaIVBzuIOGSoDmJBVEQ3rUIRKoRaoVUHk5f+CE0akhQXR8G14ODPYtXBxVlXB1dBEPwBcXNzUnSREu9LCi1ifPB4H+e9c7jvXkColZhmtY0Cmm6bqURczGRXxNAruhAEMI1hmVnGrCQl4bu+7hHg512MZ/m/+3N1qzmLAQGReIYZpk28Tjy5aRuc94kjrCirxOfEIyYVSPzIdcXjN84FlwWeGTHTqTniCLFYaGGlhVnR1IgniKOqplO+kPFY5bzFWStVWKNO/sNwTl9e4jrtASSwgEVIEKGggg2UYCNGp06KhRTdx338/a5fIpdCrg0wcsyjDA2y6wefwe/eWvnxMS8pHAfaXxznYxAI7QL1quN8HztO/QQIPgNXetNfrgFTn6RXm1r0COjZBi6um5qyB1zuAH1PhmzKrsTnL+TzwPsZjSkL9N4Cnate3xr3OH0A0tSr5A1wcAgMFSh7zeffHa19+/dNo38/hq9yr+iELI0AAAAGYktHRAAAAAAAAPlDu38AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflDAsTByz7Va2cAAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aCBHSU1QV4EOFwAAAYBJREFUKM+lkjFIlVEYht/zn3sFkYYUyUnIRcemhCtCU6JQOLiIU+QeJEQg6BBIm0s4RBCBLjq5OEvgJC1uOniJhivesLx17/97/vO9b4NK4g25157hfHCGB773/cA0HZIEAKiMj+LWiOxljG/i96pnCFP58XHnrWX2+9cj0dYl9Yu2FE9/9rXrcAAgs2eSyiBfOe/XRD503h/CuffOubQVUXL+Jh9BllzBbyJJBgDclVkO4Kukd8zzkXJbeUljIldFTstsmSHM6S81ma2KfPKlFdkGAMY4wzx/bbXapMy21My+YizdKNq5mDzLkrxafSxySFKjSWX2oTmjKzz4vN0r2lOFcL/Q3V0/mX95ILMXTTGYVfaut/aP2+oCMAvnZgCcsF5fcR0dg65YHAdwB+QApADvu0AuOe/ftlJAD7Nsgmm6yBjDtfWORJZlNtFyo/lR5Z7MyheKA5ktSur7sTAHazSG27pehjAiaVfkN8b4XFIJ/wOzbOx07VNRUuHy7w98CzCcGPyWywAAAABJRU5ErkJggg==)](https://github.com/CrowdStrike/falconpy/blob/main/AUTHORS.md) + +##### Hosts API operations discussed +This sample demonstrates the following CrowdStrike Hosts API operations: + +| Operation | Description | +| :--- | :--- | +| [GetDeviceDetails](https://falconpy.io/Service-Collections/Hosts.html#getdevicedetails) | Get details on one or more hosts by providing agent IDs (AID). You can get a host's agent IDs (AIDs) from the [QueryDevicesByFilter](https://www.falconpy.io/Service-Collections/Hosts.html#querydevicesbyfilter) operation, the Falcon console or the Streaming API. | +| [QueryDevicesByFilter](https://falconpy.io/Service-Collections/Hosts.html#querydevicesbyfilter) | Search for hosts in your environment by platform, hostname, IP, and other criteria. | + +--- + #### Hosts Tagger This [example](hosts#hosts-tagger) will demonstrate how to tag or untag multiple hosts in batch. diff --git a/samples/hosts/README.md b/samples/hosts/README.md index a1bea4c6e..8a3cdb2d4 100644 --- a/samples/hosts/README.md +++ b/samples/hosts/README.md @@ -9,6 +9,7 @@ The examples in this folder focus on leveraging CrowdStrike's Hosts API to perfo - [Get Host Groups](#get-host-groups) - [Host Report](#host-report) - [Host Search](#host-search) +- [Host Search Advanced](#host-search-advanced) - [List sensor versions by Hostname](#list-sensors-by-hostname) - [List (and optionally remove) duplicate sensors](#list-duplicate-sensors) - [List (and optionally remove) stale sensors](#list-stale-sensors) @@ -362,7 +363,7 @@ The source code for these examples can be found [here](get_host_groups.py). --- -## Hosts Report +## Host Report This script replaces the manual daily export of hosts from the Falcon Console that was required to audit host compliance. It was developed to be run as a recurring job and will output a CSV with all hosts in the CID along with other required info that can then be imported into a compliance dashboard or tool. ### Running the program @@ -545,6 +546,67 @@ Required arguments: ### Example source code The source code for these examples can be found [here](host_search.py). +--- +## Host Search Advanced + +This script retains the original functionality of host_search.py above, but adds in functionality for partial matches of hostnames. This will help with endpoint discovery where the domain is known, or a pattern of host naming is known, but not all endpoints have been discovered. + +This script will also ignore comments in a hostname file, thus keeping the output.csv cleaner. + +To read an input file of hostnames, the -f option (used in the original host_search.py) has been changed to -i. This made more sense considering the more "insensitive" nature of the search, and makes a visual identification of the full command easier if you use both the original host_search.py, and the host_search_advanced.py. A potential use case could be to discover hosts using the 'advanced' search, in order to reconcile with hostname files for use with the original host search. + +#### Command-line help +Command-line help is available via the `-h` argument. + +```shell +usage: host_search_advanced.py [-h] [-d] [-n HOSTNAME] [-i INPUT_FILE] [-o OUTPUT_PATH] + [-k CLIENT_ID] [-s CLIENT_SECRET] + + _______ __ _______ __ __ __ +| _ .----.-----.--.--.--.--| | _ | |_.----|__| |--.-----. +|. 1___| _| _ | | | | _ | 1___| _| _| | <| -__| +|. |___|__| |_____|________|_____|____ |____|__| |__|__|__|_____| +|: 1 | |: 1 | +|::.. . | |::.. . | FalconPy +`-------' `-------' + + _ _ _ ____ _ + | | | | ___ ___| |_ / ___| ___ __ _ _ __ ___| |__ + | |_| |/ _ \/ __| __| \___ \ / _ \/ _` | '__/ __| '_ \ + | _ | (_) \__ \ |_ ___) | __/ (_| | | | (__| | | | + |_| |_|\___/|___/\__| |____/ \___|\__,_|_| \___|_| |_| + _ _ _ + / \ __| |_ ____ _ _ __ ___ ___ __| | + / _ \ / _` \ \ / / _` | '_ \ / __/ _ \/ _` | + / ___ \ (_| |\ V / (_| | | | | (_| __/ (_| | + /_/ \_\__,_| \_/ \__,_|_| |_|\___\___|\__,_| + + +This script will take a file listing of hostnames (one host per line) or +a single hostname provided at runtime to produce a CSV containing the +details for hosts that are found. This solution can be used to compare a +list of hostnames to the list of hosts in the Falcon Console to determine +which hostnames are not currently reporting in to the console, or to discover hosts based on a partial match of the hostname. Comments in input files are also ommitted from lookup, thus keeping the output.csv clean, and allowing you to work with more useful host name files/inventory. + +Developed by @Don-Swanson-Adobe, additional functionality by @David-M-Berry + +options: + -h, --help show this help message and exit + -d, --debug Enable API debugging + -n HOSTNAME, --hostname HOSTNAME + Hostname to search for + -i INPUT_FILE, --input_file INPUT_FILE + Text file containing hostnames to search for + -o OUTPUT_PATH, --output_path OUTPUT_PATH + Location to store CSV output + +Required arguments: + -k CLIENT_ID, --client_id CLIENT_ID + CrowdStrike Falcon API key + -s CLIENT_SECRET, --client_secret CLIENT_SECRET + CrowdStrike Falcon API secret +``` + --- @@ -746,6 +808,12 @@ This variation will retrieve a list of hosts that haven't checked in to CrowdStr python3 stale_sensors.py -k $FALCON_CLIENT_ID -s $FALCON_CLIENT_SECRET -d 30 -t testtag ``` +This variation leverages a regular expression to match the host "SDKDEMO3". + +```shell +python3 stale_sensors.py -k $FALCON_CLIENT_ID -s $FALCON_CLIENT_SECRET -d 30 -p "^SDK.*3$" +``` + You can reverse the list sort with the `-r` or `--reverse` argument. ```shell @@ -762,7 +830,8 @@ Command-line help is available via the `-h` argument. ```shell % python3 stale_sensors.py -h -usage: stale_sensors.py [-h] -k CLIENT_ID -s CLIENT_SECRET [-m MSSP] [-g] [-d DAYS] [-r] [-x] [-t TAG] +usage: stale_sensors.py [-h] -k CLIENT_ID -s CLIENT_SECRET [-m MSSP] [-g] [-d DAYS] [-r] [-x] [-t TAG] [-c] [-o OUTPUT_FILE] [-q] + [-f {windows,mac,linux,k8s}] [-p HOSTFILTER] CrowdStrike Unattended Stale Sensor Environment Detector. @@ -786,6 +855,10 @@ results for the US-GOV-1 region, pass the '-g' argument. - ray.heffer@crowdstrike.com; 03.29.22 - Added new argument for Grouping Tags (--grouping, -g) - @morcef, jshcodes@CrowdStrike; 06.05.22 - More reasonable date calcs, Linting, Easier arg parsing Easier base_url handling, renamed grouping_tag to tag +- jshcodes@Crowdstrike; 11.02.22 - Added CSV output options and cleaner date outputs. +- nmills@forbarr; 22.05.24 - Fixed deprecation warning on date function, + Added new arg to accept hostname pattern + Batch the call to hide_hosts to avoid API error optional arguments: -h, --help show this help message and exit @@ -799,6 +872,14 @@ optional arguments: -r, --reverse Reverse sort (defaults to ASC) -x, --remove Remove hosts identified as stale -t TAG, --tag TAG Falcon Grouping Tag name for the hosts + -c, --csv Export results to CSV + -o OUTPUT_FILE, --output_file OUTPUT_FILE + File to output CSV results to. Ignored when "-c" is not specified. + -q, --quotes Quote non-numeric fields in CSV output. + -f {windows,mac,linux,k8s}, --filter-by-os {windows,mac,linux,k8s} + OS filter (windows, macos, linux) + -p HOSTFILTER, --host-pattern HOSTFILTER + filter hostnames by regex ``` ### Example source code @@ -1307,4 +1388,4 @@ Required arguments: ### Example source code The source code for these examples can be found [here](serial_search.py). ---- \ No newline at end of file +--- diff --git a/samples/hosts/host_search.py b/samples/hosts/host_search.py index e433a1d88..149d47082 100755 --- a/samples/hosts/host_search.py +++ b/samples/hosts/host_search.py @@ -22,6 +22,7 @@ which hostnames are not currently reporting in to the console. Developed by @Don-Swanson-Adobe +Modification: 05.28.24 - David M. Berry - Updated get_hostnames function to ignore comments. """ import os import logging @@ -66,16 +67,20 @@ def consume_arguments() -> Namespace: def get_hostnames(target_file: str): - """Open CSV and import serials.""" + """Open file and import hostnames, ignoring comments.""" try: - with open(target_file, newline='') as host_file: + with open(target_file, 'r') as host_file: print("Opening hostname file") - return host_file.read().splitlines() - + hostnames = [] + for line in host_file: + line = line.split('#')[0].strip() # Remove comments and strip whitespace + if line: # Ignore empty lines + hostnames.append(line) + return hostnames except FileNotFoundError: raise SystemExit( "You must provide a valid hostname file with the '-f' argument, " - "or a host with the '-h' argument in order to run this program." + "or a host with the '-n' argument in order to run this program." ) diff --git a/samples/hosts/host_search_advanced.py b/samples/hosts/host_search_advanced.py new file mode 100644 index 000000000..0c5423f1e --- /dev/null +++ b/samples/hosts/host_search_advanced.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python3 +r""" + _______ __ _______ __ __ __ +| _ .----.-----.--.--.--.--| | _ | |_.----|__| |--.-----. +|. 1___| _| _ | | | | _ | 1___| _| _| | <| -__| +|. |___|__| |_____|________|_____|____ |____|__| |__|__|__|_____| +|: 1 | |: 1 | +|::.. . | |::.. . | FalconPy +`-------' `-------' + + _ _ _ ____ _ + | | | | ___ ___| |_ / ___| ___ __ _ _ __ ___| |__ + | |_| |/ _ \/ __| __| \___ \ / _ \/ _` | '__/ __| '_ \ + | _ | (_) \__ \ |_ ___) | __/ (_| | | | (__| | | | + |_| |_|\___/|___/\__| |____/ \___|\__,_|_| \___|_| |_| + _ _ _ + / \ __| |_ ____ _ _ __ ___ ___ __| | + / _ \ / _` \ \ / / _` | '_ \ / __/ _ \/ _` | + / ___ \ (_| |\ V / (_| | | | | (_| __/ (_| | + /_/ \_\__,_| \_/ \__,_|_| |_|\___\___|\__,_| + + +This script will take a file listing of hostnames (one host per line) or +a single hostname provided at runtime to produce a CSV containing the +details for hosts that are found. This solution can be used to compare a +list of hostnames to the list of hosts in the Falcon Console to determine +which hostnames are not currently reporting in to the console, or to discover hosts based on a partial match of the hostname. Comments in input files are also ommitted from lookup, thus keeping the output.csv clean, and allowing you to work with more useful host name files/inventory. + +Developed by @Don-Swanson-Adobe, additional functionality by @David-M-Berry +""" + + +import os +import logging +from argparse import ArgumentParser, RawTextHelpFormatter, Namespace +from falconpy import Hosts + + +def consume_arguments() -> Namespace: + """Consume any provided command line arguments.""" + parser = ArgumentParser(description=__doc__, formatter_class=RawTextHelpFormatter) + parser.add_argument("-d", "--debug", + help="Enable API debugging", + action="store_true", + default=False + ) + parser.add_argument("-n", "--hostname", + help="Hostname to search for", + required=False # Make this argument optional + ) + parser.add_argument("-i", "--input_file", # Add a new argument for input file + help="Text file containing hostnames to search for" + ) + parser.add_argument("-o", "--output_path", + help="Location to store CSV output", + default="output.csv" + ) + req = parser.add_argument_group("Required arguments") + req.add_argument("-k", "--client_id", + help="CrowdStrike Falcon API key", + default=os.getenv("FALCON_CLIENT_ID") + ) + req.add_argument("-s", "--client_secret", + help="CrowdStrike Falcon API secret", + default=os.getenv("FALCON_CLIENT_SECRET") + ) + parsed = parser.parse_args() + if not parsed.client_id or not parsed.client_secret: + parser.error("You must provide CrowdStrike API credentials using the '-k' and '-s' arguments.") + + return parsed + + +def process_hostnames(hostnames): + with open(cmd_line.output_path, 'w') as file_object: # Open in 'write' mode to clear previous content + with Hosts(client_id=cmd_line.client_id, + client_secret=cmd_line.client_secret, + debug=cmd_line.debug, + pythonic=True + ) as falcon: + for hostname in hostnames: + host_ids = falcon.query_devices_by_filter(filter=f"hostname:*'*{hostname}*'") + if host_ids: + for device in falcon.get_device_details(ids=host_ids.data): + device_hostname = device["hostname"] + last_seen = device["last_seen"] + rfm = device["reduced_functionality_mode"] + cid = device["cid"] + tags = device["tags"] + tag = ",".join(tag_string.replace('SensorGroupingTags/', '') for tag_string in tags) + file_object.write(f"{device_hostname},{cid},{rfm},{last_seen},{tag}\n") + else: + file_object.write(f"{hostname},HOST NOT FOUND\n") + + # Deduplicate the output file + deduplicate_output(cmd_line.output_path) + print(f"Search complete, results have been written to {cmd_line.output_path}") + + +def deduplicate_output(output_path): + """Remove duplicate entries from the output file.""" + with open(output_path, 'r') as file: + lines = file.readlines() + unique_lines = set(lines) + with open(output_path, 'w') as file: + file.writelines(unique_lines) + + +def prepend_header(output_path): + """Prepend the header line to the output file.""" + with open(output_path, 'r+') as file: + content = file.read() + file.seek(0, 0) + file.write("Hostname,CID,RFM,Last Seen,Landscape,Tag1,Tag2,Tag3,Tag4\n" + content) + + +cmd_line = consume_arguments() + +if cmd_line.hostname: + hostnames = [cmd_line.hostname] +elif cmd_line.input_file: + with open(cmd_line.input_file, 'r') as file: + hostnames = [] + for line in file: + line = line.split('#')[0].strip() # Remove comments and strip whitespace + if line: # Ignore empty lines + hostnames.append(line) +else: + print("You must provide either a hostname or an input file.") + exit(1) + + +if cmd_line.debug: + logging.basicConfig(level=logging.DEBUG) + +process_hostnames(hostnames) +prepend_header(cmd_line.output_path) diff --git a/samples/hosts/stale_sensors.py b/samples/hosts/stale_sensors.py index be9dca774..1dabf7531 100644 --- a/samples/hosts/stale_sensors.py +++ b/samples/hosts/stale_sensors.py @@ -21,8 +21,12 @@ - @morcef, jshcodes@CrowdStrike; 06.05.22 - More reasonable date calcs, Linting, Easier arg parsing Easier base_url handling, renamed grouping_tag to tag - jshcodes@Crowdstrike; 11.02.22 - Added CSV output options and cleaner date outputs. +- nmills@forbarr; 22.05.24 - Fixed deprecation warning on date function, + Added new arg to accept hostname pattern + Batch the call to hide_hosts to avoid API error """ import csv +import re from argparse import ArgumentParser, RawTextHelpFormatter from datetime import datetime, timedelta, timezone from dateutil import parser as dparser @@ -35,7 +39,6 @@ "Please execute `python3 -m pip install crowdstrike-falconpy` and try again." ) from no_falconpy - def parse_command_line() -> object: """Parse command-line arguments and return them back as an ArgumentParser object.""" parser = ArgumentParser( @@ -74,7 +77,8 @@ def parse_command_line() -> object: '-d', '--days', help='Number of days since a host was seen before it is considered stale', - required=False + required=False, + default=10 ) parser.add_argument( '-r', @@ -130,9 +134,15 @@ def parse_command_line() -> object: required=False, dest="osfilter" ) + parser.add_argument( + "-p", "--host-pattern", + help="filter hostnames by regex", + default=r".*", + required=False, + dest="hostfilter" + ) return parser.parse_args() - def connect_api(key: str, secret: str, base_url: str, child_cid: str = None) -> Hosts: """Connect to the API and return an instance of the Hosts Service Class.""" return Hosts(client_id=key, client_secret=secret, base_url=base_url, member_cid=child_cid) @@ -156,6 +166,10 @@ def get_hosts(date_filter: str, tag_filter: str, os_filter: str) -> list: if os_filter == "K8s": os_filter = "K8S" filter_string = f"{filter_string} + platform_name:'{os_filter}'" + x = falcon.query_devices_by_filter_scroll( + limit=5000, + filter=filter_string + )["body"]["resources"] return falcon.query_devices_by_filter_scroll( limit=5000, filter=filter_string @@ -164,7 +178,7 @@ def get_hosts(date_filter: str, tag_filter: str, os_filter: str) -> list: def calc_stale_date(num_days: int) -> str: """Calculate the 'stale' datetime based upon the number of days provided by the user.""" - today = datetime.utcnow() + today = datetime.now(timezone.utc) return str(today - timedelta(days=num_days)).replace(" ", "T") @@ -212,14 +226,15 @@ def hide_hosts(id_list: list) -> dict: # List to hold our identified hosts stale = [] # For each stale host identified -try: - for host in get_host_details(get_hosts(STALE_DATE, args.tag, args.osfilter)): - # Retrieve host detail - stale = parse_host_detail(host, stale) -except KeyError as api_error: - raise SystemExit( - "Unable to communicate with CrowdStrike API, check credentials and try again." - ) from api_error +pattern = args.hostfilter +if args.hostfilter != r".*": + print(f"Pattern is: {re.escape(pattern)}") + +for host in get_host_details(get_hosts(STALE_DATE, args.tag, args.osfilter)): + # Retrieve host detail + if 'hostname' in host: + if re.findall(pattern, host['hostname']): + stale = parse_host_detail(host, stale) # If we produced stale host results if stale: @@ -245,12 +260,15 @@ def hide_hosts(id_list: list) -> dict: else: # Remove the hosts host_list = [x[1] for x in stale] - remove_result = hide_hosts(host_list) - if remove_result["status_code"] == 202: - for deleted in remove_result["body"]["resources"]: - print(f"Removed host {deleted['id']}") - else: - for deleted in remove_result["body"]["errors"]: - print(f"[{deleted['code']}] {deleted['message']}") + batch_size = 50 + for i in range(0, len(host_list), batch_size): + batch = host_list[i:i + batch_size] + remove_result = hide_hosts(batch) + if remove_result["status_code"] == 202: + for deleted in remove_result["body"]["resources"]: + print(f"Removed host {deleted['id']}") + else: + for deleted in remove_result["body"]["errors"]: + print(f"[{deleted['code']}] {deleted['message']}") else: print("No stale hosts identified for the range specified.") diff --git a/samples/sensor_download/download_sensor.py b/samples/sensor_download/download_sensor.py index 99b430326..5260f9678 100644 --- a/samples/sensor_download/download_sensor.py +++ b/samples/sensor_download/download_sensor.py @@ -178,7 +178,12 @@ def create_constants(): filter=OS_FILTER, sort="version.desc" ) -if CMD in "list": + +if sensors["status_code"] == 401: + raise SystemExit("Authentication failure (STATUS CODE {})".format(sensors["status_code"])) + + +elif CMD in "list": # List sensors data = [] headers = {