Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Version 1.1 - Expanded binary responses, action_parameter abstraction, docstrings #648

Merged
merged 35 commits into from
May 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
0bc4a72
Unit testing updates
jshcodes Apr 26, 2022
c5b9974
Fix docstring typo. Closes #621.
jshcodes Apr 26, 2022
776fd96
Fix workflow version specification error
jshcodes Apr 26, 2022
a69e64f
Bump version -> 1.0.11.dev1
jshcodes Apr 26, 2022
5d0593f
Add linting workflows to primary branches
jshcodes Apr 26, 2022
66b67a8
Fix docstring typo. Closes #640.
jshcodes May 2, 2022
7251319
Update docstring typo. Closes #634.
jshcodes May 3, 2022
ec2720e
Almost useful module listing and method counts
jshcodes May 4, 2022
1181304
Rename public_modules.sh
jshcodes May 4, 2022
ca4082f
Add ExpandedResult class
jshcodes May 5, 2022
4871c59
Add result expansion for binary responses
jshcodes May 5, 2022
cd9ff0b
Add results expansion support
jshcodes May 5, 2022
706e2bc
Expand testing to cover new code paths
jshcodes May 5, 2022
3db19cc
Bump version -> 1.1.0.dev1
jshcodes May 5, 2022
b733905
Update CHANGELOG.md
jshcodes May 5, 2022
a23de8f
Almost useful string search utility
jshcodes May 8, 2022
67e2f57
Update README.md
jshcodes May 8, 2022
7864107
Add group_id action_parameter handler.
jshcodes May 9, 2022
17ea422
Add filter action_parameters handler.
jshcodes May 9, 2022
8db0c3d
Add payload handler for incident action_parameters
jshcodes May 9, 2022
47b2c40
Add incident action_parameters handler.
jshcodes May 9, 2022
c0b2998
Fix doctring typo.
jshcodes May 9, 2022
73198f1
Update docstrings to reflect changes from #647.
jshcodes May 9, 2022
2d63195
Updated handling of unassign key. Relates to #647.
jshcodes May 9, 2022
94cc222
Allow list support for tagging parameters.
jshcodes May 9, 2022
ca252d3
Update docstrings to reflect new functionality.
jshcodes May 9, 2022
90081a3
Add group_id action parameter handler.
jshcodes May 9, 2022
40b1a17
Add note action_parameter handler.
jshcodes May 9, 2022
4b3b52f
Update CHANGELOG.md
jshcodes May 9, 2022
f0d79b2
Add babel fish
jshcodes May 9, 2022
ee694b3
Revert note action_parameter handler.
jshcodes May 9, 2022
6ca55bf
Expand unit testing to cover new code paths.
jshcodes May 9, 2022
87e5b3e
Update CHANGELOG.md
jshcodes May 9, 2022
fc81871
Update wordlist
jshcodes May 9, 2022
1e50b7f
Bump version -> 1.1.0
jshcodes May 9, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -719,3 +719,4 @@ GUID
CTO
Wonk
wonk
wb
4 changes: 4 additions & 0 deletions .github/workflows/bandit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@ on:
- '**.py'
branches:
- main
- dev
- samples
- 'ver_*'
pull_request:
paths:
- '**.py'
branches:
- main
- dev
- samples
- 'ver_*'

jobs:
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/flake8.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@ on:
- '**.py'
branches:
- main
- dev
- samples
- 'ver_*'
pull_request:
paths:
- '**.py'
branches:
- main
- dev
- samples
- 'ver_*'

jobs:
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/pydocstyle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ on:
- '**.py'
branches:
- main
- dev
- 'ver_*'
pull_request:
paths:
- '**.py'
branches:
- main
- dev
- 'ver_*'

jobs:
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/pylint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@ on:
- '**.py'
branches:
- main
- dev
- samples
- 'ver_*'
pull_request:
paths:
- '**.py'
branches:
- main
- dev
- samples
- 'ver_*'

jobs:
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/unit_testing_ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,21 @@ on:
branches:
- main
- 'ver_*'
- dev
pull_request:
paths:
- '**.py'
branches:
- main
- 'ver_*'
- dev

jobs:
build:
strategy:
matrix:
# os: [macos-latest, windows-latest, ubuntu-latest]
python-version: ['3.6', '3.7', '3.8', '3.9', '3.10']
python-version: ['3.6', '3.7', '3.8', '3.9', '3.10', '3.11-dev']
# runs-on: ${{ matrix.os }}
runs-on: ubuntu-latest
steps:
Expand Down
57 changes: 57 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,60 @@
# Version 1.1.0
## Added features and functionality
+ Added: Results object expansion - expanded results are returned as a tuple, Ex: `(status_code, headers, content)`. This allows for
headers and status to be checked on binary API returns. Expanded results are supported for all calls to the API and can be requested from
any Service Class method or the Uber Class __command__ method using the keyword `expand_result`.
- `_result.py`
- `_util.py`
- `api_complete.py`
- `test_sample_uploads.py`

__Example__
```python
# Pass a boolean True to the `expand_result` keyword to request expanded results.
download_result = samples.get_sample(ids=file_sha, expand_result=True)

# We're returned a tuple (status, headers, content)
# Status will be in 0
print(f"Status returned: {download_result[0]}")
# Headers will be in 1
print(f"Headers returned: {download_result[1]}")
# File content will be in 2
with open(example_file, "wb") as download_file:
download_file.write(download_result[2])
```

+ Added: Specify action_parameters keys for __perform__ operations using keywords instead of a list of dictionaries.
* Keyword: `group_id`
- `device_control_policies.py` (_perform_action_ method)
- `firewall_policies.py` (_perform_action_ method)
- `prevention_policy.py` (_perform_policies_action_ method)
- `response_policies.py` (_perform_policies_action_ method)
- `sensor_update_policy.py` (_perform_policies_action_ method)
* Keyword: `filter`
- `host_group.py` (_perform_group_action_ method)
* Keywords: `add_tag`, `delete_tag`, `unassign`, `update_name`, `update_assigned_to_v2`, `update_description`, `update_status`
- `_payload/__init__.py`
- `_payload/_incidents.py`
- `incidents.py` (_perform_incident_action_ method)

## Other
+ Fixed: Docstring typo in sort options for `query_accounts` and `query_logins` methods within the Discover Service Class.
- `discover.py`
+ Fixed: Docstring typo not listing `id` requirements for keyword submissions to the `indicator_update` method within the IOC Service Class.
- `ioc.py`
+ Fixed: Docstring typo listing an incorrect return type for the `get_download` operation within the ReportExecutions Service Class.
- `report_executions.py`
+ Fixed: Docstring typo in Real Time Response Service Class referencing non-existent `action_parameters` payload element.
- `real_time_response.py`
+ Added: Babel fish operation ID to endpoint translator.
- `util/babel_fish.py`
+ Added: FalconPy terminal word search utility.
- `util/find-strings.sh`
+ Added: FalconPy module listing utility.
- `util/public-modules.sh`
+ Added: FalconPy version check utility.
- `util/vcheck.sh`

# Version 1.0.10
## Added features and functionality
+ Added: New versions of two operations within the Real Time Response Service Class. `list_files_v2` and `delete_file_v2` are used the same as the original methods, but provide more results detail. You should leverage `delete_file_v2` if you are retrieving files using `list_files_v2`.
Expand Down
3 changes: 2 additions & 1 deletion src/falconpy/_payload/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from ._recon import recon_notifications_payload, recon_rule_preview_payload
from ._malquery import malquery_exact_search_payload, malquery_hunt_payload, malquery_fuzzy_payload
from ._detects import update_detects_payload
from ._incidents import incident_action_parameters
from ._ioa import ioa_exclusion_payload, ioa_custom_payload
from ._prevention_policy import prevention_policy_payload
from ._sensor_update_policy import sensor_policy_payload
Expand Down Expand Up @@ -56,5 +57,5 @@
"cspm_policy_payload", "cspm_scan_payload", "device_policy_payload", "falconx_payload",
"mssp_payload", "ioa_custom_payload", "firewall_policy_payload", "firewall_container_payload",
"firewall_rule_group_payload", "firewall_rule_group_update_payload", "reports_payload",
"activity_payload", "case_payload"
"activity_payload", "case_payload", "incident_action_parameters"
]
105 changes: 105 additions & 0 deletions src/falconpy/_payload/_incidents.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
"""Internal payload handling library - Incident Payloads.

_______ __ _______ __ __ __
| _ .----.-----.--.--.--.--| | _ | |_.----|__| |--.-----.
|. 1___| _| _ | | | | _ | 1___| _| _| | <| -__|
|. |___|__| |_____|________|_____|____ |____|__| |__|__|__|_____|
|: 1 | |: 1 |
|::.. . | CROWDSTRIKE FALCON |::.. . | FalconPy
`-------' `-------'

OAuth2 API - Customer SDK

This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.

In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.

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 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.

For more information, please refer to <https://unlicense.org>
"""


def incident_action_parameters(passed_keywords: dict) -> dict:
"""Create a properly formatted action_parameters branch for incident action payload operations.

Available keywords
add_tag - Adds the associated value as a new tag on all the incidents of the ids list.
Multiple tags may be provided as a list or comma delimited string.
delete_tag - Deletes tags matching the value from all the incidents in the ids list.
Multiple tags may be provided as a list or a comma delimited string.
unassign - Unassigns all users from all of the incidents in the ids list. Boolean.
This action does not require a value parameter. For example:
"action_parameters": [
{
"name": "unassign"
}
]
update_name - Updates the name to the parameter value of all the incidents in the ids list. String.
update_assigned_to_v2 - Assigns the user matching the UUID in the parameter value to all of
the incidents in the ids list. String.
For information on getting the UUID of a user, see Find existing users.
update_description - Updates the description to the parameter value of all the incidents listed
in the ids list. String.
update_status - Updates the status to the parameter value of all the incidents in the ids list.
Integer string. Valid status values are 20, 25, 30, or 40:
20: New
25: Reopened
30: In Progress
40: Closed
[
{
"name": "string",
"value": "string"
},
{
"name": "string",
"value": "string"
},
etc.
]
"""
returned_payload = []
valid_keywords = [
"add_tag", "delete_tag", "unassign", "update_name", "update_assigned_to_v2"
"update_description", "update_status"
]
for key, val in passed_keywords.items():
if key in valid_keywords and key != "unassign":
if key in ["add_tag", "delete_tag"]:
if isinstance(val, str):
val = val.split(",")
for tag_val in val:
returned_payload.append({
"name": key,
"value": tag_val
})
else:
returned_payload.append({
"name": key,
"value": val
})
if key == "unassign":
if val:
returned_payload.append({
"name": key
})

return returned_payload
20 changes: 19 additions & 1 deletion src/falconpy/_result.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""API Response formatting class.
"""API Response formatting classes.

_______ __ _______ __ __ __
| _ .----.-----.--.--.--.--| | _ | |_.----|__| |--.-----.
Expand Down Expand Up @@ -53,3 +53,21 @@ def __call__(self: object, status_code: int, headers, body: dict) -> dict:
self.result_obj['body'] = body

return self.result_obj


class ExpandedResult:
"""Callable subsclass to handle parsing of expanded result client output."""

def __init__(self: object) -> tuple:
"""Instantiate the subclass and intialize the expanded result object."""
self.result_tuple = ()

def __call__(self: object, status_code: int, headers, content) -> tuple:
"""Format ingested values into a properly formatted expanded result object."""
content_result = content
if isinstance(content, dict):
content_result = content["body"]

self.result_tuple = (status_code, dict(headers), content_result)

return self.result_tuple
30 changes: 23 additions & 7 deletions src/falconpy/_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
import urllib3
from urllib3.exceptions import InsecureRequestWarning
from ._version import _TITLE, _VERSION
from ._result import Result
from ._result import Result, ExpandedResult
from ._base_url import BaseURL
from ._uber_default_preference import PREFER_NONETYPE
urllib3.disable_warnings(InsecureRequestWarning)
Expand Down Expand Up @@ -186,7 +186,10 @@ def service_request(caller: object = None, **kwargs) -> object: # May return di


@force_default(defaults=["headers"], default_types=["dict"])
def perform_request(endpoint: str = "", headers: dict = None, **kwargs) -> object: # May return dict or object datatypes
def perform_request(endpoint: str = "", # pylint: disable=R0912
headers: dict = None,
**kwargs
) -> object: # May return dict or object data types
"""Leverage the requests library to perform the requested CrowdStrike OAuth2 API operation.

Keyword arguments:
Expand Down Expand Up @@ -218,11 +221,14 @@ def perform_request(endpoint: str = "", headers: dict = None, **kwargs) -> objec
- Example: (5.05, 25)
user_agent: string
- Example: companyname-integrationname/version
expand_result: bool - Enable expanded results output
- Example: True
"""
method = kwargs.get("method", "GET")
body = kwargs.get("body", None)
body_validator = kwargs.get("body_validator", None)
user_agent = kwargs.get("user_agent", None)
expand_result = kwargs.get("expand_result", False)
perform = True
if method.upper() in _ALLOWED_METHODS:
# Validate parameters
Expand Down Expand Up @@ -253,11 +259,18 @@ def perform_request(endpoint: str = "", headers: dict = None, **kwargs) -> objec
proxies=kwargs.get("proxy", None), timeout=kwargs.get("timeout", None)
)

if response.headers.get('content-type') == "application/json":
returned = Result()(response.status_code, response.headers, response.json())
returning_content_type = response.headers.get('content-type')
if returning_content_type == "application/json":
content_return = Result()(response.status_code, response.headers, response.json())
else:
content_return = response.content
# Expanded results allow for status code and header checks on binary returns
if expand_result:
returned = ExpandedResult()(response.status_code, response.headers, content_return)
else:
returned = response.content
except JSONDecodeError:
returned = content_return

except JSONDecodeError: # pragma: no cover
# No response content, but a successful request was made
returned = generate_ok_result(
message="No content returned",
Expand Down Expand Up @@ -381,6 +394,7 @@ def process_service_request(calling_object: object,
distinct_field -- Field name to retrieve distinct values for (Sensor Update Policies API)
body_validator -- Dictionary containing details regarding body payload validation
body_required -- List of required body payload parameters
expand_result -- Request expanded results output
"""
target_endpoint = [ep for ep in endpoints if operation_id == ep[0]][0]
target_url = f"{calling_object.base_url}{target_endpoint[2]}"
Expand All @@ -398,6 +412,7 @@ def process_service_request(calling_object: object,
if passed_keywords or passed_params:
parameter_payload = args_to_params(passed_params, passed_keywords, endpoints, operation_id)
passed_headers = kwargs.get("headers", None) if kwargs.get("headers", None) else calling_object.headers
expand_result = passed_keywords.get("expand_result", False) if passed_keywords else kwargs.get("expand_result", False)
new_keywords = {
"caller": calling_object,
"method": target_method,
Expand All @@ -409,7 +424,8 @@ def process_service_request(calling_object: object,
"data": kwargs.get("data", None),
"files": kwargs.get("files", None),
"body_validator": kwargs.get("body_validator", None),
"body_required": kwargs.get("body_required", None)
"body_required": kwargs.get("body_required", None),
"expand_result": expand_result
}

return service_request(**new_keywords)
Expand Down
2 changes: 1 addition & 1 deletion src/falconpy/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

For more information, please refer to <https://unlicense.org>
"""
_VERSION = '1.0.10'
_VERSION = '1.1.0'
_MAINTAINER = 'Joshua Hiller'
_AUTHOR = 'CrowdStrike'
_AUTHOR_EMAIL = '[email protected]'
Expand Down
Loading