Science Score: 44.0%

This score indicates how likely this project is to be science-related based on various indicators:

  • CITATION.cff file
    Found CITATION.cff file
  • codemeta.json file
    Found codemeta.json file
  • .zenodo.json file
    Found .zenodo.json file
  • DOI references
  • Academic publication links
  • Committers with academic emails
  • Institutional organization owner
  • JOSS paper metadata
  • Scientific vocabulary similarity
    Low similarity (11.8%) to scientific vocabulary
Last synced: 7 months ago · JSON representation ·

Repository

Basic Info
  • Host: GitHub
  • Owner: CrowdStrike
  • License: mit
  • Language: Python
  • Default Branch: main
  • Size: 1.18 MB
Statistics
  • Stars: 5
  • Watchers: 6
  • Forks: 2
  • Open Issues: 0
  • Releases: 11
Created over 2 years ago · Last pushed 10 months ago
Metadata Files
Readme License Code of conduct Citation Security Support

README.md

CrowdStrike Logo.

Falcon Foundry Function as a Service Python FDK

foundry-fn-python is a community-driven, open source project designed to enable the authoring of functions. While not a formal CrowdStrike product, the foundry-fn-python project and the crowdstrike-foundry-function FDK package are maintained by CrowdStrike and supported in partnership with the open source developer community.

Installation ⚙️

Installation via pip

The FDK can be installed or updated via pip install:

shell python3 -m pip install crowdstrike-foundry-function

Quickstart 💫

Example Code

Add the FDK to your project by following the installation instructions above, then create your main.py that contains your handler implementation:

```python from crowdstrike.foundry.function import ( APIError, Request, Response, Function, )

func = Function.instance()

An example POST handler

@func.handler(method='POST', path='/my-resource') def on_post(request: Request) -> Response:

# Validate the request body
if 'name' not in request.body:
    # This example expects 'name' field in the request body.
    # Return an error response (400 - Bad Request) if not provided by the caller
    return Response(
        code=400,
        errors=[APIError(code=400, message='name field is missing from request body')]
    )

# Process the request
new_resource_id = 1
# ...snip...

# Return a success response
return Response(
    body={
       'result': f'Resource with name {request.body["name"]} is created.',
       'id': new_resource_id
    },
    code=200,
)

if name == 'main': func.run() ```

Example breakdown

The Function object

The Function class wraps the Foundry Function implementation. A Function instance can consist of one or more handlers, with each corresponding to an endpoint. You should only have one Function object defined per function implemented within your Foundry application. Multiple instances will result in unexpected behavior.

python func = Function.instance()

The function handler decorator

The handler decorator defines a Python method as handler for a specific endpoint. This handler must have the method and path keywords defined. The method keyword will correspond to one of the supported HTTP methods (GET, POST, PUT, PATCH or DELETE). The path keyword will define the URL used to trigger this method, and should be unique.

python @func.handler(method='POST', path='/my-resource')

Method details - Request

Our python handler function is decorated with @func.handler. The first argument to our method must be a Request object which defines the HTTP request payload and metadata.

A Request object consists of:

  • body: The request payload as given in the Function Gateway body payload field. This will be deserialized as a dictionary (dict[str, Any]).
  • params: The request headers (params.header) and query string parameters (params.query).
  • url: The request path relative to the function. This is a string.
  • method: The request HTTP method or verb.
  • access_token: Caller-supplied access token.

In this example we've named our method on_post, but you may name the method whatever you wish.

python def on_post(request: Request) -> Response:

Method details - Response

The return type for our method should be a Response object.

Successful responses

A successful response will be a Response object containing the fields body (a dictionary containing the response returned to the function) and code (the HTTP status code returned to the function).

```python

Return a success response

return Response( body={ 'result': f'Resource with name {request.body["name"]} is created.', 'id': newresourceid }, code=200, ) ```

Error responses

An unsuccessful response will be a Response object containing the fields errors (a list of APIError objects) and code (the HTTP status code returned to the function).

An APIError object will contain a code indicating the type of the error and a message which should contain the error text.

If no code is provided as part of the Response object, this value will be derived from the greatest valid HTTP code present within the APIError list.

python return Response( code=400, errors=[APIError(code=400, message='id field is missing from request params')] )

Running the function

The runner method is the general starting point for execution of your function and will be executed when your code is called by Foundry. This causes the Function to initialize and start execution. This should be the last line of your script as code defined after the func.run() statement may not be executed. You may implement code before this statement as necessary.

python if __name__ == '__main__': func.run()

Retrieving parameters passed to your Function

You may retrieve query string values passed to your function by accessing the request.params.query dictionary.

python resource_id = request.params.query.get("id")

Additional HTTP method examples

Different types of HTTP requests will follow the same pattern demonstrated in our POST request above.

HTTP GET

```python from crowdstrike.foundry.function import ( APIError, Request, Response, Function, )

func = Function.instance()

An example GET handler

@func.handler(method='GET', path='/my-resource') def on_get(request: Request) -> Response:

# Fetch the requested resources
resources = []
# ...snip...

# Return the requested resources
return Response(
    body={'resources': resources},
    code=200,
)

if name == 'main': func.run() ```

HTTP PUT

```python from crowdstrike.foundry.function import ( APIError, Request, Response, Function, )

func = Function.instance()

An example PUT handler

@func.handler(method='PUT', path='/my-resource') def on_put(request: Request) -> Response:

# Obtain the id of the resource to update from the request query parameters
resource_id = request.params.query.get('id')
if not resource_id:
    # This example expects 'id' field in the request query parameters.
    # Returns an error response (400 - Bad Request) if not provided by the caller
    return Response(
        code=400,
        errors=[APIError(code=400, message='id field is missing from request params')]
    )

# Get the update data provided in the request body and
# Update the resource with the data provided
data = request.body.get('data')
# ...snip...

# Return success with the updated resource info
return Response(
    body={
       'result': f'Resource {resource_id} is updated successfully.',
       'data': data
    },
    code=200,
)

if name == 'main': func.run() ```

HTTP DELETE

```python from crowdstrike.foundry.function import ( APIError, Request, Response, Function, )

func = Function.instance()

An example DELETE handler

@func.handler(method='DELETE', path='/my-resource') def on_delete(request: Request) -> Response:

# Obtain the id of the resource to update from the request query parameters
resource_id = request.params.query.get('id')
if not resource_id:
    # This example expects 'id' field in the request query parameters.
    # Returns an error response (400 - Bad Request) if not provided by the caller
    return Response(
        code=400,
        errors=[APIError(code=400, message='id field is missing from request params')]
    )

# Delete the requested resource
# ...snip...

# Return success back to the caller
return Response(
    code=200,
)

if name == 'main': func.run() ```

Testing locally

The FDK provides an out-of-the-box runtime for executing the function.

Executing your code

[!NOTE] A basic HTTP server will be started to listen on port 8081 when executing your code locally.

shell cd my-project python3 main.py

You can use curl or another python application to make requests to the web server that has been started.

Example POST request

```shell

Test POST /my-resource request

curl --location 'http://localhost:8081' \ -H 'Content-Type: application/json' \ --data '{ "body": { "name": "bar" }, "method": "POST", "url": "/my-resource" }' ```

Example GET request

```shell

Test GET /my-resource request

curl --location 'http://localhost:8081' \ -H 'Content-Type: application/json' \ --data '{ "method": "GET", "url": "/my-resource" }' ```

Example PUT request

```shell

Test PUT /my-resource request

curl --location 'http://localhost:8081' \ -H 'Content-Type: application/json' \ --data '{ "body": { "name": "bar", }, "params": { "query": { "id": "12345" } }, "method": "PUT", "url": "/my-resource" }' ```

Example DELETE request

```shell

Test DELETE /my-resource request

curl --location 'http://localhost:8081' \ -H 'Content-Type: application/json' \ --data '{ "params": { "query": { "id": "12345" } }, "method": "DELETE", "url": "/my-resource" }' ```

Executing your code without an HTTP server

If you prefer to test your function locally without starting an HTTP server, you can provide the request payload in a JSON file on the command line.

First, create a JSON file containing your request payload. Example request_payload.json file: shell { "body": { "name": "bar" }, "method": "POST", "url": "/my-resource" }

Then invoke your function handler as follows:

shell python3 main.py --data ./request_payload.json

This will execute the requested function handler and print the response returned, including the response status code, body and headers.

You can also provide request headers to your function on the command line: shell python3 main.py --data request_payload.json --header "Content-Type: application/json" --header "X-CUSTOM-HEADER: testing"

Leveraging the FalconPy SDK to interact with CrowdStrike APIs inside of your Foundry function

Foundry function authors should include crowdstrike-falconpy within their requirements.txt file and then import falconpy explicitly in their function code.

You may use any FalconPy Service Class or the FalconPy Uber Class within your function.

General FalconPy usage information

FalconPy implements Context Authentication for use within Foundry Functions, removing the need for developers to provide their access_token to the class as this value is provided by context when the function is executed.

[!TIP] If you are instantiating a FalconPy class within your method, you will need to do this for every method you implement. If you instantiate the FalconPy class outside of your method, but before the func.run() statement, this object will be available to all methods defined in your function code.

To test the function locally without having to adjust your code, you can set the following environment variables in your local environment:

| Variable Name | Purpose | | :--- | :--- | | FALCON_CLIENT_ID | CrowdStrike Falcon API client ID | | FALCON_CLIENT_SECRET | CrowdStrike Falcon API client secret |

FalconPy usage example

```python

from falconpy import Hosts from crowdstrike.foundry.function import ( Function, Request, Response, APIError )

func = Function.instance()

@func.handler(method='POST', path='/hosts-query') def onhostsquery(request: Request) -> Response:

# get the requested Host IDs from the request body
host_ids = request.body.get("ids")
if not host_ids:
   return Response(
        code=400,
        errors=[APIError(code=400, message='Required host ids are not provided')]
    )

# Initialize falconpy client for Hosts API
# This example uses context authentication
hosts_client = Hosts()

# Call falconpy API to fetch the details of the requested hosts
api_result = hosts_client.get_device_details_v1(ids=host_ids)
if api_result['status_code'] != 200:
    # falconpy API returned an error
    response = Response(
       code=api_result['status_code'],
        errors=[
           APIError(code=api_result['status_code'], message="falconpy API call failed")
        ]
    )
else:
   # falconpy API was successful, return the requested data
    response = Response(
        body={
            'hosts': api_result['body']['resources']
        },
        code=200
    )

return response

if name == 'main': func.run() ```

Using custom configurations and debug logging

Foundry supports custom configurations and debug logging to support developers with the implementation of their functions.

Implementing custom configurations

Using a custom configuration within a function is optional and may be provided as a JSON file. This functionality is intended to give the developer a location to store custom configuration data, such as API keys and credentials, in a secure manner when the function is deployed on Falcon platform.

[!NOTE] The configuration is encrypted with a unique key per function in the cloud.

To utilize a custom configuration within a function, include the config keyword argument as shown in the example below.

The config keyword is an optional argument to the handler function and must be the second argument if provided.

Enabling logging

Logging for a function is optional but adding log messages to functions can make triage and debugging easier when troubleshooting problems. When a function is deployed on the Falcon platform, the messages logged with the provided logger are formatted in a custom manner with fields injected to assist with working within the Falcon logging infrastructure.

[!NOTE] You may use native FalconPy logging in conjunction with your function logger config by providing the debug keyword when you instantiate your FalconPy class.

To utilize logging in a function, include the logger parameter as shown in the example below.

logger is an optional parameter to the handler function and must the third parameter if provided.

```python from logging import Logger from typing import Union, Any from falconpy import Hosts from crowdstrike.foundry.function import ( Function, Request, Response, APIError )

func = Function.instance()

@func.handler(method='POST', path='/hosts-query') def onhostsquery(request: Request, config: Union[dict[str, Any], None], logger: Logger) -> Response:

logger.info("POST handler for /hosts-query is invoked")

# get the requested Host IDs from the request body
host_ids = request.body.get("ids")
if not host_ids:
   logger.error("ids argument is missing from request parameters")
   return Response(
        code=400,
        errors=[APIError(code=400, message='Required host ids are not provided')]
    )

# Example config provided to the function
action = "Dev resource update"
if config and config.get("is_production", False):
    action = "Production resource update"

# Initialize falconpy client for Hosts API and enable debugging
hosts_client = Hosts(debug=True)

# Call falconpy API to fetch the details of the requested hosts
api_result = hosts_client.get_device_details_v1(ids=host_ids)
if api_result['status_code'] != 200:
    # FalconPy SDK returned an error
    response = Response(
       code=api_result['status_code'],
        errors=[
           APIError(code=api_result['status_code'], message="FalconPy API call failed")
        ]
    )
else:
   # falconpy API was successful, return the requested data
    response = Response(
        body={
            'hosts': api_result['body']['resources'],
            'action': action
        },
        code=200
    )

return response

if name == 'main': func.run() ```



WE STOP BREACHES

Owner

  • Name: CrowdStrike
  • Login: CrowdStrike
  • Kind: organization
  • Email: github@crowdstrike.com
  • Location: United States of America

Citation (CITATION.cff)

cff-version: 1.2.0
title: 'CrowdStrike Foundry Function as a Service for Python'
message: >-
  If you use this software, and wish to cite the origins, 
  please use metadata from this file.
type: software
authors:
  - given-names: 
    family-names: CrowdStrike
    email: foundry-fn-python@crowdstrike.com
  - given-names: John
    family-names: Stone
    affiliation: CrowdStrike
  - given-names: Chris
    family-names: Cannon
    affiliation: CrowdStrike
  - given-names: Johnny
    family-names: Steenbergen
    affiliation: CrowdStrike
  - given-names: Banu
    family-names: Yuceer
    affiliation: CrowdStrike
  - given-names: Joshua
    family-names: Hiller
    affiliation: CrowdStrike
repository-code: 'https://github.com/CrowdStrike/foundry-fn-python'
url: 'https://www.crowdstrike.com'
repository-artifact: 'https://pypi.org/project/crowdstrike-foundry-function/'
abstract: >-
  The CrowdStrike Foundry Function as a Service SDK
  is a community-driven, open source project designed
  to enable the authoring of functions within the
  CrowdStrike Foundry ecosystem.
keywords:
  - crowdstrike
  - oauth2
  - crowdstrike-api
  - crowdstrike-falcon-api
  - crowdstrike-foundry
  - crowdstrike-faas
  - python
  - windows
  - linux
  - mac
license: MIT

GitHub Events

Total
  • Release event: 1
  • Watch event: 4
  • Delete event: 2
  • Push event: 8
  • Pull request review event: 2
  • Pull request event: 7
  • Fork event: 1
  • Create event: 5
Last Year
  • Release event: 1
  • Watch event: 4
  • Delete event: 2
  • Push event: 8
  • Pull request review event: 2
  • Pull request event: 7
  • Fork event: 1
  • Create event: 5

Committers

Last synced: 9 months ago

All Time
  • Total Commits: 19
  • Total Committers: 5
  • Avg Commits per committer: 3.8
  • Development Distribution Score (DDS): 0.421
Past Year
  • Commits: 5
  • Committers: 3
  • Avg Commits per committer: 1.667
  • Development Distribution Score (DDS): 0.4
Top Committers
Name Email Commits
johns31459 1****9 11
Chris Cannon c****n@c****m 5
Praveen Bathala p****a@c****m 1
Joshua Hiller 7****s 1
Banu Yuceer b****r@c****m 1
Committer Domains (Top 20 + Academic)

Issues and Pull Requests

Last synced: 7 months ago

All Time
  • Total issues: 1
  • Total pull requests: 16
  • Average time to close issues: about 1 hour
  • Average time to close pull requests: 17 days
  • Total issue authors: 1
  • Total pull request authors: 3
  • Average comments per issue: 0.0
  • Average comments per pull request: 0.06
  • Merged pull requests: 15
  • Bot issues: 0
  • Bot pull requests: 0
Past Year
  • Issues: 1
  • Pull requests: 7
  • Average time to close issues: about 1 hour
  • Average time to close pull requests: 13 days
  • Issue authors: 1
  • Pull request authors: 3
  • Average comments per issue: 0.0
  • Average comments per pull request: 0.0
  • Merged pull requests: 7
  • Bot issues: 0
  • Bot pull requests: 0
Top Authors
Issue Authors
Pull Request Authors
  • johns31459 (16)
  • jshcodes (2)
  • BanuY (1)
  • prvn (1)
Top Labels
Issue Labels
Pull Request Labels

Packages

  • Total packages: 1
  • Total downloads:
    • pypi 54,355 last-month
  • Total dependent packages: 0
  • Total dependent repositories: 0
  • Total versions: 11
  • Total maintainers: 4
pypi.org: crowdstrike-foundry-function

CrowdStrike Foundry Function Software Developer Kit for Python

  • Versions: 11
  • Dependent Packages: 0
  • Dependent Repositories: 0
  • Downloads: 54,355 Last month
Rankings
Dependent packages count: 7.6%
Average: 38.5%
Dependent repos count: 69.5%
Maintainers (4)
Last synced: 8 months ago

Dependencies

.github/workflows/python-package.yml actions
  • actions/checkout v3 composite
  • actions/setup-python v3 composite
pyproject.toml pypi
requirements.txt pypi
  • astroid ==2.15.5
  • build ==0.10.0
  • certifi ==2023.7.22
  • charset-normalizer ==3.1.0
  • coverage ==7.2.7
  • crowdstrike-falconpy ==1.2.16
  • dill ==0.3.6
  • exceptiongroup ==1.1.1
  • idna ==3.4
  • iniconfig ==2.0.0
  • isort ==5.12.0
  • lazy-object-proxy ==1.9.0
  • mccabe ==0.7.0
  • packaging ==23.1
  • platformdirs ==3.8.0
  • pluggy ==1.2.0
  • pylint ==2.17.4
  • pyproject_hooks ==1.0.0
  • pytest ==7.4.0
  • requests ==2.31.0
  • tomli ==2.0.1
  • tomlkit ==0.11.8
  • typing_extensions ==4.6.3
  • urllib3 ==1.26.16
  • wrapt ==1.15.0
setup.py pypi