Skip to content

Commit

Permalink
Version 0.1.2 (#28)
Browse files Browse the repository at this point in the history
* 💄 Return pretty results instead of print to stdout

* 🚀 New example: find_stale_sensors

* 🚀 New example: show_agent_versions

* 🔊 Update logging

* ✨ Demonstrate context manager usage

* 💡 Fix comment

* 🚚 Rename examples/get_devices -> examples/hosts

* 🚀 New Sample: list_sensor_versions.py

* 📝 Expand config.example.yml

* ✨ Add examples as pyproject.toml scripts

* ➕ Add new dev dependencies

* 🔖 Bump version -> 0.1.2

* 📝 Update README.md

* 🔊 Remove unnecessary carriage return

* 🥅 Add custom error handling

* 🥅 Add custom error handling

* 🚨 Appeasing the linting overlords

* 🚸 Usability enhancements for poetry execution

* 🚨 More genuflecting at the linting bots

* 🚨 More appeasements for the lords of lint

* ✨ Specify example profile name via poetry arguments

* 🐛 Fix default user_agent specification

* ✏️ Fix message typo

* 🥅 Add no matches returned error handler

* ✏️ 🚨 Fix typo / linting

* 🥅 Add no devices found error handler

* 📝 🚨 Docstring linting

* 🥅 Add no devices found error handler

* ✨ New example: List hidden devices

* 🥅 Add no logins found error handler

* ✨ New Example: List Login History

* 🥅 Add no addresses found exception

* ✨ New Example: List Network History

* ✏️ Fix typo

* 🚸 Add example execution timer class

* 🔊 Add execution time logging

* ✅ Update unit testing

* 🚨 YAML lint

* ✨ Add poetry script lookup

* 🚨 Linting

* 🎨 Fiddling with the timing calculations

* ♻️ More pythonic performance timer

* ⬆️ Update dependencies

* 🍻 💡 🚨 Comments for all the things!

* 🍻 🚨 💡 Mean ol' linters

* 🍻 🎨 💡 🚨 It's art dude, relax!

* 🍻 💡 Add missing pagination style to comments

* ✨ Initial host group functionality

* 🚧 ✨ New Example: List all Host Groups

* 🚨 I shake my fist at the whitespace

* 🚚 Move list-poetry-scripts.sh -> list-examples.sh

* 📝 Update README.md

* 🍻 ✨ 🧑‍💻 Dunders may actually be evil...

* 🍻 💡 🧑‍💻 I love Unicode sometimes

* 🔊 Add Caracara identifier to custom user_agent

* 🍻 🎨 📝 Update flash

* Merges README Changes

* ⚰️ Clean up example

* ✨ Add HOST_GROUP_SCROLL_BATCH_SIZE

* ✨ Add GenericAPIError exception class

* ✨ Host Group add / remove member functionality

* ✨ Add Example: List All Group Members

* 🚨 Lint lord appeasement

* General code clean up and efficieny work

* ✨ 🥅 Add new exception handlers

* ✨ Add delete host group functionality

* General linting and bug fixes

* 🥅 ✨ Add new exception handlers

* 🥅 ✨ Implement graceful exception handling

* ✏️ Fix typo

* 🚨 Fix linting

* 🚨 Fix linting

* 🥅  ⚰️ Remove unnecessary error handler

* 🥅 Multiple missing arguments exception handler

* ✨ Add host group CRUD functionality

* Full group member retrieval

* ⏪️ Revert previous changes

* ♻️ Refactored solution, ✨ New example (IDs only)

* ✨ Add Example: List all group member IDs

* ♻️ Leaning on the faster endpoint solution

* 🚨 Fix linting

* 🚨 Fix docstring lint

* ♻️ Slight refactor

* Move filters to an instance variable to avoid conflicts between multiple FalconFilter instances

* ➖➕ Temp move to crowdstrike-falconpy-dev package

* ✨ Add describe_states method

* ✨ New Example: List all device states

* Refactoring, type hinting and general code clean up to appease linters and human developers alike

* Address recommendations made by pydocstyle

* Resolves linting issues in dependabot.yml

* 📌 Update to dependencies and migration back to crowdstrike-falconpy

* 💡 Fixes up some comments in hosts.py

* 👷 Use Poetry pre-releases to pass through CI

* Pins container and Python matrix versions

* Fixes parsing of Python 3.10

Co-authored-by: Joshua Hiller <[email protected]>
  • Loading branch information
ChristopherHammond13 and jshcodes authored Jul 27, 2022
1 parent e4fe342 commit eee1022
Show file tree
Hide file tree
Showing 60 changed files with 2,620 additions and 716 deletions.
2 changes: 1 addition & 1 deletion .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ updates:
schedule:
interval: weekly
open-pull-requests-limit: 10
- package-ecosystem: github-actions
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: monthly
Expand Down
9 changes: 6 additions & 3 deletions .github/workflows/bandit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ on:
- 'ver_*'

jobs:
analyze:
runs-on: ubuntu-latest
bandit-analyze:
strategy:
matrix:
python-version: ["3.8", "3.10"]
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
Expand All @@ -25,7 +28,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install poetry
python -m pip install poetry --pre
poetry install
- name: Analyze package with bandit
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
language: [ 'python' ]

# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
Expand Down
9 changes: 6 additions & 3 deletions .github/workflows/flake8.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ on:
- 'ver_*'

jobs:
analyze:
runs-on: ubuntu-latest
flake8:
strategy:
matrix:
python-version: ["3.8", "3.10"]
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
Expand All @@ -25,7 +28,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install poetry
python -m pip install poetry --pre
poetry install
- name: Lint package source with flake8
run: |
Expand Down
9 changes: 6 additions & 3 deletions .github/workflows/pydocstyle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ on:
- 'ver_*'

jobs:
analyze:
runs-on: ubuntu-latest
pydocstyle:
strategy:
matrix:
python-version: ["3.8", "3.10"]
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
Expand All @@ -25,7 +28,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install poetry
python -m pip install poetry --pre
poetry install
- name: Check package docstrings with pydocstyle
run: |
Expand Down
9 changes: 6 additions & 3 deletions .github/workflows/pylint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ on:
- 'ver_*'

jobs:
analyze:
runs-on: ubuntu-latest
pylint:
strategy:
matrix:
python-version: ["3.8", "3.10"]
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
Expand All @@ -25,7 +28,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install poetry
python -m pip install poetry --pre
poetry install
- name: Lint package source with pylint
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install poetry
python -m pip install poetry --pre
- name: Build and publish
run: |
poetry config pypi-token.pypi ${{ secrets.PACKAGE_API_SECRET }}
Expand Down
59 changes: 50 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
![CrowdStrike Falcon](https://raw.githubusercontent.com/CrowdStrike/falconpy/main/docs/asset/cs-logo.png) [![Twitter URL](https://img.shields.io/twitter/url?label=Follow%20%40CrowdStrike&style=social&url=https%3A%2F%2Ftwitter.com%2FCrowdStrike)](https://twitter.com/CrowdStrike)<br/>

# Caracara



<!--
![PyPI - Status](https://img.shields.io/pypi/status/caracara)
[![PyPI](https://img.shields.io/pypi/v/caracara)](https://pypi.org/project/caracara/)
[![Pylint](https://github.com/CrowdStrike/caracara/actions/workflows/pylint.yml/badge.svg)](https://github.com/CrowdStrike/caracara/actions/workflows/pylint.yml)
[![Flake8](https://github.com/CrowdStrike/caracara/actions/workflows/flake8.yml/badge.svg)](https://github.com/CrowdStrike/caracara/actions/workflows/flake8.yml)
[![Bandit](https://github.com/CrowdStrike/caracara/actions/workflows/bandit.yml/badge.svg)](https://github.com/CrowdStrike/caracara/actions/workflows/bandit.yml)
[![CodeQL](https://github.com/CrowdStrike/caracara/actions/workflows/codeql.yml/badge.svg)](https://github.com/CrowdStrike/caracara/actions/workflows/codeql.yml)
-->
[![PyPI](https://img.shields.io/pypi/v/caracara)](https://pypi.org/project/caracara/)
![OSS Lifecycle](https://img.shields.io/osslifecycle/CrowdStrike/caracara)

A friendly wrapper to help you interact with the CrowdStrike Falcon API. Less code, less fuss, better performance, and full interoperability with [FalconPy](https://github.com/CrowdStrike/falconpy/).
Expand Down Expand Up @@ -83,14 +86,13 @@ python3 -m pip uninstall caracara

</details>

## Basic Usage Example
## Basic Usage Examples

```python
"""
This example will use the API credentials provided as parameters to
list the names of all systems within your Falcon tenant that run Windows.
"""List Windows devices.
The example demonstrates how to use the FalconFilter() class and the Hosts API.
This example will use the API credentials provided as keywords to list the
IDs and hostnames of all systems within your Falcon tenant that run Windows.
"""

from caracara import Client
Expand All @@ -104,7 +106,34 @@ filters = client.FalconFilter()
filters.create_new_filter("OS", "Windows")

response_data = client.hosts.describe_devices(filters)
print(f"Found {len(response_data.keys())} devices running Windows")
print(f"Found {len(response_data)} devices running Windows")

for device_id, device_data in response_data.items():
hostname = device_data.get("hostname", "Unknown Hostname")
print(f"{device_id} - {hostname}")
```

You can also leverage the built in context manager and environment variables.

```python
"""List stale sensors.
This example will use the API credentials set in the environment to list the
hostnames and IDs of all systems within your Falcon tenant that have not checked
into your CrowdStrike tenant within the past 7 days.
This is determined based on the filter LastSeen less than or equal (LTE) to 7 days ago (-7d).
"""

from caracara import Client


with Client(client_id="${CLIENT_ID_ENV_VARIABLE}", client_secret="${CLIENT_SECRET_ENV_VARIABLE}") as client:
filters = client.FalconFilter()
filters.create_new_filter("LastSeen", "-7d", "LTE")
response_data = client.hosts.describe_devices(filters)

print(f"Found {len(response_data)} stale devices")

for device_id, device_data in response_data.items():
hostname = device_data.get("hostname", "Unknown Hostname")
Expand All @@ -117,7 +146,7 @@ Each API wrapper is provided alongside example code. Cloning or downloading/extr

Using the examples collection requires that you install our Python packaging tool of choice, [Poetry](https://python-poetry.org). Please refer to the Poetry project's [installation guide](https://python-poetry.org/docs/#installation) if you do not yet have Poetry installed.

Once Poetry is installed, make sure you run `poetry install` within the `caracara` folder to set up the Python virtual environment.
Once Poetry is installed, make sure you run `poetry install` within the root repository folder to set up the Python virtual environment.

To configure the examples, first copy `examples/config.example.yml` to `examples/config.yml`. Then, add your API credentials and example-specific settings to `examples/config.yml`. Once you have set up profiles for each Falcon tenant you want to test with, execute examples using one of the two options below.

Expand Down Expand Up @@ -146,6 +175,14 @@ If you do not want to enter the Caracara virtual environment (e.g., because you
poetry run examples/get_devices/list_windows_devices.py
```

All examples are also configured in the `pyproject.toml` file as scripts, allowing them to be executed simply.

```shell
poetry run stale-sensors
```

> To get a complete list of available examples, execute the command `util/list-examples.sh` from the root of the repository folder.
</details>

## Documentation
Expand All @@ -154,4 +191,8 @@ __*Coming soon!*__

## Contributing

Interested in taking part in the development of the Caracara project? Start [here](CONTRIBUTING.md).
Interested in taking part in the development of the Caracara project? Start [here](CONTRIBUTING.md).

## Why Caracara?

Simple! We like birds at CrowdStrike, so what better bird to name a Python project after one that eats just about anything, including snakes :)
44 changes: 43 additions & 1 deletion caracara/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,22 @@
"""Main Caracara module. Exposes an API Client to interface with Falcon."""
r"""The CrowdStrike Developer ToolKit for FalconPy.
Welcome to the
______ __ _______ __ __ __
| |.----.-----.--.--.--.--| | __| |_.----.|__| |--.-----.
| ---|| _| _ | | | | _ |__ | _| _|| | <| -__|
|______||__| |_____|________|_____|_______|____|__| |__|__|__|_____|
.,-::::: :::. :::::::.. :::. .,-::::: :::. :::::::.. :::.
,;;;'````' ;;`;; ;;;;``;;;; ;;`;; ,;;;'````' ;;`;; ;;;;``;;;; ;;`;;
[[[ ,[[ '[[, [[[,/[[[' ,[[ '[[, [[[ ,[[ '[[, [[[,/[[[' ,[[ '[[,
$$$ c$$$cc$$$c $$$$$$c c$$$cc$$$c $$$ c$$$cc$$$c $$$$$$c c$$$cc$$$c
`88bo,__,o, 888 888,888b "88bo,888 888,`88bo,__,o, 888 888,888b "88bo,888 888,
"YUMMMMMP"YMM ""` MMMM "W" YMM ""` "YUMMMMMP"YMM ""` MMMM "W" YMM ""`
Developer Toolkit for FalconPy
"""
__all__ = ["Client", "Policy"]

from caracara.client import Client
Expand All @@ -8,3 +26,27 @@
# According to PEP 8, dunders should be before imports; however,
# this import is needed for the dunder to function
__version__ = _pkg_version

"""
MIT License
Copyright (c) CrowdStrike
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
39 changes: 28 additions & 11 deletions caracara/client.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
"""Caracara API Client."""
r"""Caracara base API interface client.
CrowdStrike Caracara
_______ __ __ __
| _ | |__.-----.-----| |_
|. 1___| | | -__| | _|
|. |___|__|__|_____|__|__|____|
|: 1 |
|::.. . | for FalconPy
`-------'
"""

import logging

Expand Down Expand Up @@ -26,16 +36,14 @@


class Client:
"""
Falcon API client.
"""Falcon API client.
This class exposes all available Falcon API commands, and proxies requests through
the FalconPy library.
"""

class FalconFilter(FalconFilter):
"""
Falcon Filter wrapper class.
"""Falcon Filter wrapper class.
Create a sub-class of the FalconFilter class which we own locally within
the client. This allows us to dynamically calculate the FQL filter attributes
Expand Down Expand Up @@ -81,13 +89,23 @@ def __init__( # pylint: disable=R0913,R0914,R0915
interpolator = VariableInterpolator()
cloud_name = interpolator.interpolate(cloud_name)
client_id = interpolator.interpolate(client_id)
client_secret = interpolator.interpolate(client_secret)
member_cid = interpolator.interpolate(member_cid)
user_agent = interpolator.interpolate(user_agent)
proxy = interpolator.interpolate(proxy)
user_agent = interpolator.interpolate(user_agent)

if user_agent:
user_agent = f"{user_agent} ({user_agent_string()})"
else:
user_agent = user_agent_string()

self.logger.debug("User agent: %s", user_agent)

auth_keys = {
"base_url": cloud_name,
"client_id": client_id,
"client_secret": interpolator.interpolate(client_secret),
"client_secret": client_secret,
"member_cid": member_cid,
"proxy": proxy,
"user_agent": user_agent,
Expand All @@ -108,10 +126,6 @@ def __init__( # pylint: disable=R0913,R0914,R0915
if auth_keys[k] is None:
del auth_keys[k]

if not user_agent:
user_agent = user_agent_string()
self.logger.debug("User agent: %s", user_agent)

self.verbose = verbose
self.logger.debug("Verbose mode: %s", verbose)

Expand All @@ -127,8 +141,11 @@ def __init__( # pylint: disable=R0913,R0914,R0915
self.api_authentication = falconpy_authobject
else:
raise Exception("Impossible authentication scenario")

self.logger.info("Requesting API token")
self.api_authentication.token() # Need to force the authentication to resolve the base_url
self.logger.info("Resolved Base URL: %s", self.api_authentication.base_url)

# Configure modules here so that IDEs can pick them up
self.logger.debug("Setting up Hosts module")
self.hosts = HostsApiModule(self.api_authentication)
Expand Down Expand Up @@ -162,7 +179,7 @@ def __enter__(self):

def __exit__(self, *args):
"""Discard our token when we exit the context."""
self.logger.info("Revoking API token")
self.logger.info("Discarding API token on Client exit")
self.api_authentication.revoke(self.api_authentication.token_value)
self.logger.debug("Discarding Caracara context manager")
return args
8 changes: 5 additions & 3 deletions caracara/common/batching.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"""
Caracara batching support.
"""Caracara batching support.
This module contains all the required code to generically parallelise retrieval of data batches.
Expand Down Expand Up @@ -35,7 +34,10 @@ def batch_data_pull_threads() -> int:
return thread_count


def batch_get_data(lookup_ids: str, func: Callable[[object, List[str], str], Dict]):
def batch_get_data(
lookup_ids: str,
func: Callable[[object, List[str], str], Dict],
) -> Dict[str, Dict]:
"""Retrieve details for the list of AIDs provided.
Arguments
Expand Down
Loading

0 comments on commit eee1022

Please sign in to comment.