From 4d8c36b27e5406900e4479845122c3b41d97e44e Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Sun, 11 Apr 2021 00:12:35 -0400 Subject: [PATCH 01/29] Custom IOA service class - initial commit --- src/falconpy/custom_ioa.py | 336 +++++++++++++++++++++++++++++++++++++ 1 file changed, 336 insertions(+) create mode 100644 src/falconpy/custom_ioa.py diff --git a/src/falconpy/custom_ioa.py b/src/falconpy/custom_ioa.py new file mode 100644 index 000000000..f901dc9d9 --- /dev/null +++ b/src/falconpy/custom_ioa.py @@ -0,0 +1,336 @@ +""" + _______ __ _______ __ __ __ +| _ .----.-----.--.--.--.--| | _ | |_.----|__| |--.-----. +|. 1___| _| _ | | | | _ | 1___| _| _| | <| -__| +|. |___|__| |_____|________|_____|____ |____|__| |__|__|__|_____| +|: 1 | |: 1 | +|::.. . | CROWDSTRIKE FALCON |::.. . | FalconPy +`-------' `-------' + +OAuth2 API - Customer SDK + +custom_ioa - Falcon Custom Indicators of Attack API Interface Class + +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 +""" +from ._util import service_request, parse_id_list +from ._service_class import ServiceClass + + +class Custom_IOA(ServiceClass): + """The only requirement to instantiate an instance of this class + is a valid token provided by the Falcon API SDK OAuth2 class. + """ + def get_patterns(self: object, ids) -> dict: + """Get pattern severities by ID""" + # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/custom-ioa/get-patterns + ID_LIST = str(parse_id_list(ids)).replace(",", "&ids=") + FULL_URL = self.base_url+"/ioarules/entities/pattern-severities/v1?ids={}".format(ID_LIST) + HEADERS = self.headers + returned = service_request(caller=self, + method="GET", + endpoint=FULL_URL, + headers=HEADERS, + verify=self.ssl_verify + ) + return returned + + def get_platformsMixin0(self: object, ids) -> dict: + """Get platforms by ID""" + # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/custom-ioa/get-platformsMixin0 + ID_LIST = str(parse_id_list(ids)).replace(",", "&ids=") + FULL_URL = self.base_url+"/ioarules/entities/platforms/v1?ids={}".format(ID_LIST) + HEADERS = self.headers + returned = service_request(caller=self, + method="GET", + endpoint=FULL_URL, + headers=HEADERS, + verify=self.ssl_verify + ) + return returned + + def get_rule_groupsMixin0(self: object, ids) -> dict: + """Get rule groups by ID""" + # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/custom-ioa/get-rule-groupsMixin0 + ID_LIST = str(parse_id_list(ids)).replace(",", "&ids=") + FULL_URL = self.base_url+"/ioarules/entities/rule-groups/v1?ids={}".format(ID_LIST) + HEADERS = self.headers + returned = service_request(caller=self, + method="GET", + endpoint=FULL_URL, + headers=HEADERS, + verify=self.ssl_verify + ) + return returned + + def create_rule_groupMixin0(self: object, body: dict, cs_username: str) -> dict: + """Create a rule group for a platform with a name and an optional description. Returns the rule group.""" + # [POST] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/custom-ioa/create-rule-groupMixin0 + FULL_URL = self.base_url+'/ioarules/entities/rule-groups/v1' + HEADERS = self.headers + HEADERS['X-CS-USERNAME'] = cs_username + BODY = body + returned = service_request(caller=self, + method="POST", + endpoint=FULL_URL, + body=BODY, + headers=HEADERS, + verify=self.ssl_verify + ) + return returned + + def delete_rule_groupMixin0(self: object, ids, cs_username: str, parameters: dict = {}) -> dict: + """Delete rule groups by ID.""" + # [DELETE] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/custom-ioa/delete-rule-groupsMixin0 + ID_LIST = str(parse_id_list(ids)).replace(",", "&ids=") + FULL_URL = self.base_url+'/ioarules/entities/rule-groups/v1?ids={}'.format(ID_LIST) + HEADERS = self.headers + HEADERS['X-CS-USERNAME'] = cs_username + PARAMS = parameters + returned = service_request(caller=self, + method="DELETE", + endpoint=FULL_URL, + params=PARAMS, + headers=HEADERS, + verify=self.ssl_verify + ) + return returned + + def update_rule_groupMixin0(self: object, body: dict, cs_username: str) -> dict: + """Update a rule group. The following properties can be modified: name, description, enabled.""" + # [PATCH] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/custom-ioa/update-rule-groupMixin0 + FULL_URL = self.base_url+'/ioarules/entities/rule-groups/v1' + HEADERS = self.headers + HEADERS['X-CS-USERNAME'] = cs_username + BODY = body + returned = service_request(caller=self, + method="PATCH", + endpoint=FULL_URL, + body=BODY, + headers=HEADERS, + verify=self.ssl_verify + ) + return returned + + def get_rule_types(self: object, ids) -> dict: + """Get rule types by ID""" + # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/custom-ioa/get-rule-types + ID_LIST = str(parse_id_list(ids)).replace(",", "&ids=") + FULL_URL = self.base_url+"/ioarules/entities/rule-types/v1?ids={}".format(ID_LIST) + HEADERS = self.headers + returned = service_request(caller=self, + method="GET", + endpoint=FULL_URL, + headers=HEADERS, + verify=self.ssl_verify + ) + return returned + + def get_rules_get(self: object, ids) -> dict: + """Get rules by ID and optionally version in the following format: ID[:version]""" + # [POST] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/custom-ioa/get-rules-get + FULL_URL = self.base_url+"/ioarules/entities/rules/GET/v1" + HEADERS = self.headers + BODY = {} + BODY["ids"] = parse_id_list(ids).split(",") # We need a list in this scenario + returned = service_request(caller=self, + method="POST", + endpoint=FULL_URL, + body=BODY, + headers=HEADERS, + verify=self.ssl_verify + ) + return returned + + def get_rulesMixin0(self: object, ids) -> dict: + """Get rules by ID and optionally version in the following format: ID[:version]. + The max number of IDs is constrained by URL size. + """ + # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/custom-ioa/get-rulesMixin0 + ID_LIST = str(parse_id_list(ids)).replace(",", "&ids=") + FULL_URL = self.base_url+"/ioarules/entities/rules/v1?ids={}".format(ID_LIST) + HEADERS = self.headers + returned = service_request(caller=self, + method="GET", + endpoint=FULL_URL, + headers=HEADERS, + verify=self.ssl_verify + ) + return returned + + def create_rule(self: object, body: dict, cs_username: str) -> dict: + """Create a rule within a rule group. Returns the rule.""" + # [POST] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/custom-ioa/create-rule + FULL_URL = self.base_url+'/ioarules/entities/rules/v1' + HEADERS = self.headers + HEADERS['X-CS-USERNAME'] = cs_username + BODY = body + returned = service_request(caller=self, + method="POST", + endpoint=FULL_URL, + body=BODY, + headers=HEADERS, + verify=self.ssl_verify + ) + return returned + + def delete_rules(self: object, ids, cs_username: str, parameters: dict = {}) -> dict: + """Delete rules from a rule group by ID.""" + # [DELETE] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/custom-ioa/delete-rules + ID_LIST = str(parse_id_list(ids)).replace(",", "&ids=") + FULL_URL = self.base_url+'/ioarules/entities/rules/v1?ids={}'.format(ID_LIST) + HEADERS = self.headers + HEADERS['X-CS-USERNAME'] = cs_username + PARAMS = parameters + returned = service_request(caller=self, + method="DELETE", + endpoint=FULL_URL, + params=PARAMS, + headers=HEADERS, + verify=self.ssl_verify + ) + return returned + + def update_rules(self: object, body: dict, cs_username: str) -> dict: + """Update rules within a rule group. Return the updated rules.""" + # [PATCH] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/custom-ioa/update-rules + FULL_URL = self.base_url+'/ioarules/entities/rules/v1' + HEADERS = self.headers + HEADERS['X-CS-USERNAME'] = cs_username + BODY = body + returned = service_request(caller=self, + method="PATCH", + endpoint=FULL_URL, + body=BODY, + headers=HEADERS, + verify=self.ssl_verify + ) + return returned + + def validate(self: object, body: dict) -> dict: + """Validates field values and checks for matches if a test string is provided.""" + # [POST] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/custom-ioa/validate + FULL_URL = self.base_url+'/ioarules/entities/rules/validate/v1' + HEADERS = self.headers + BODY = body + returned = service_request(caller=self, + method="POST", + endpoint=FULL_URL, + body=BODY, + headers=HEADERS, + verify=self.ssl_verify + ) + return returned + + def query_patterns(self: object, parameters: dict = {}) -> dict: + """Get all pattern severity IDs""" + # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/custom-ioa/query-patterns + FULL_URL = self.base_url+"/ioarules/queries/pattern-severities/v1" + HEADERS = self.headers + PARAMS = parameters + returned = service_request(caller=self, + method="GET", + endpoint=FULL_URL, + headers=HEADERS, + params=PARAMS, + verify=self.ssl_verify + ) + return returned + + def query_platformsMixin0(self: object, parameters: dict = {}) -> dict: + """Get all platform IDs.""" + # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/custom-ioa/query-platformsMixin0 + FULL_URL = self.base_url+"/ioarules/queries/platforms/v1" + HEADERS = self.headers + PARAMS = parameters + returned = service_request(caller=self, + method="GET", + endpoint=FULL_URL, + headers=HEADERS, + params=PARAMS, + verify=self.ssl_verify + ) + return returned + + def query_rule_groups_full(self: object, parameters: dict = {}) -> dict: + """Find all rule groups matching the query with optional filter.""" + # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/custom-ioa/query-rule-groups-full + FULL_URL = self.base_url+"/ioarules/queries/rule-groups-full/v1" + HEADERS = self.headers + PARAMS = parameters + returned = service_request(caller=self, + method="GET", + endpoint=FULL_URL, + headers=HEADERS, + params=PARAMS, + verify=self.ssl_verify + ) + return returned + + def query_rule_groupsMixin0(self: object, parameters: dict = {}) -> dict: + """Finds all rule group IDs matching the query with optional filter.""" + # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/custom-ioa/query-rule-groupsMixin0 + FULL_URL = self.base_url+"/ioarules/queries/rule-groups/v1" + HEADERS = self.headers + PARAMS = parameters + returned = service_request(caller=self, + method="GET", + endpoint=FULL_URL, + headers=HEADERS, + params=PARAMS, + verify=self.ssl_verify + ) + return returned + + def query_rule_types(self: object, parameters: dict = {}) -> dict: + """Get all rule type IDs.""" + # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/custom-ioa/query-rule-types + FULL_URL = self.base_url+"/ioarules/queries/rule-types/v1" + HEADERS = self.headers + PARAMS = parameters + returned = service_request(caller=self, + method="GET", + endpoint=FULL_URL, + headers=HEADERS, + params=PARAMS, + verify=self.ssl_verify + ) + return returned + + def query_rulesMixin0(self: object, parameters: dict = {}) -> dict: + """Finds all rule IDs matching the query with optional filter.""" + # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/custom-ioa/query-rulesMixin0 + FULL_URL = self.base_url+"/ioarules/queries/rules/v1" + HEADERS = self.headers + PARAMS = parameters + returned = service_request(caller=self, + method="GET", + endpoint=FULL_URL, + headers=HEADERS, + params=PARAMS, + verify=self.ssl_verify + ) + return returned From 89c2145f553a57a9167f1f3aae6466106b84a174 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Sun, 11 Apr 2021 00:12:54 -0400 Subject: [PATCH 02/29] Unit test for Custom IOA service class --- tests/test_custom_ioa.py | 95 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 tests/test_custom_ioa.py diff --git a/tests/test_custom_ioa.py b/tests/test_custom_ioa.py new file mode 100644 index 000000000..19580d47a --- /dev/null +++ b/tests/test_custom_ioa.py @@ -0,0 +1,95 @@ +# test_custom_ioa.py +# This class tests the custom_ioa service class + +# import json +import os +import sys +# import datetime +# import pytest +# Authentication via the test_authorization.py +from tests import test_authorization as Authorization + +# Import our sibling src folder into the path +sys.path.append(os.path.abspath('src')) +# Classes to test - manually imported from sibling folder +from falconpy import custom_ioa as FalconIOA + +auth = Authorization.TestAuthorization() +auth.serviceAuth() +falcon = FalconIOA.Custom_IOA(access_token=auth.token) +AllowedResponses = [200, 201, 207, 429] # Adding rate-limiting as an allowed response for now + + +class TestCSPMRegistration: + + def serviceIOA_QueryPatterns(self): + return falcon.query_patterns()["status_code"] in AllowedResponses + + def serviceIOA_QueryPlatformsMixin0(self): + return falcon.query_platformsMixin0()["status_code"] in AllowedResponses + + def serviceIOA_QueryRuleGroupsFull(self): + return falcon.query_rule_groups_full()["status_code"] in AllowedResponses + + def serviceIOA_QueryRuleGroupsMixin0(self): + return falcon.query_rule_groupsMixin0()["status_code"] in AllowedResponses + + def serviceIOA_QueryRuleTypes(self): + return falcon.query_rule_types()["status_code"] in AllowedResponses + + def serviceIOA_QueryRulesMixin0(self): + return falcon.query_rulesMixin0()["status_code"] in AllowedResponses + + def serviceIOA_GenerateErrors(self): + falcon.base_url = "nowhere" + errorChecks = True + commandList = [ + ["get_patterns", "ids='12345678'"], + ["get_platformsMixin0", "ids='12345678'"], + ["get_rule_groupsMixin0", "ids='12345678'"], + ["create_rule_groupMixin0", "body={}, cs_username='unit_testing'"], + ["delete_rule_groupMixin0", "ids='12345678', cs_username='unit_testing'"], + ["update_rule_groupMixin0", "body={}, cs_username='unit_testing'"], + ["get_rule_types", "ids='12345678'"], + ["get_rules_get", "ids='12345678'"], + ["get_rulesMixin0", "ids='12345678'"], + ["create_rule", "body={}, cs_username='unit_testing'"], + ["delete_rules", "ids='12345678', parameters={}, cs_username='unit_testing'"], + ["update_rules", "body={}, cs_username='unit_testing'"], + ["validate", "body={}"], + ["query_patterns", ""], + ["query_platformsMixin0", ""], + ["query_rule_groups_full", ""], + ["query_rule_groupsMixin0", ""], + ["query_rule_types", ""], + ["query_rulesMixin0", ""] + ] + for cmd in commandList: + if eval("falcon.{}({})['status_code']".format(cmd[0], cmd[1])) != 500: + errorChecks = False + + return errorChecks + + def test_QueryPatterns(self): + assert self.serviceIOA_QueryPatterns() is True + + def test_QueryPlatformsMixin0(self): + assert self.serviceIOA_QueryPlatformsMixin0() is True + + def test_QueryRuleGroupsFull(self): + assert self.serviceIOA_QueryRuleGroupsFull() is True + + def test_QueryRuleGroupsMixin0(self): + assert self.serviceIOA_QueryRuleGroupsMixin0() is True + + def test_QueryRuleTypes(self): + assert self.serviceIOA_QueryRuleTypes() is True + + def test_QueryRulesMixin0(self): + assert self.serviceIOA_QueryRulesMixin0() is True + + def test_Logout(self): + assert auth.serviceRevoke() is True + + def test_Errors(self): + assert self.serviceIOA_GenerateErrors() is True From a4fd1b0586086108a538a9a53f2a5fcdc393408b Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Sun, 11 Apr 2021 00:15:01 -0400 Subject: [PATCH 03/29] Enabling unit testing for push to ver_* branches --- .github/workflows/unit_testing.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/unit_testing.yml b/.github/workflows/unit_testing.yml index 7d2a9bca8..4727b2422 100644 --- a/.github/workflows/unit_testing.yml +++ b/.github/workflows/unit_testing.yml @@ -3,7 +3,9 @@ on: push: paths: - '**.py' - branches: [ main ] + branches: + - main + - 'ver_*' pull_request: paths: - '**.py' From 8e4ab1ea240bd7166437cac58e6c1cbbcc925c43 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Sun, 11 Apr 2021 00:37:19 -0400 Subject: [PATCH 04/29] Added Falcon Complete / Falcon Overwatch dashboard and MSSP endpoints --- src/falconpy/_endpoint.py | 375 ++++++++++++++++++++++++++++++++------ 1 file changed, 324 insertions(+), 51 deletions(-) diff --git a/src/falconpy/_endpoint.py b/src/falconpy/_endpoint.py index 14afd58c0..7e40b912d 100644 --- a/src/falconpy/_endpoint.py +++ b/src/falconpy/_endpoint.py @@ -104,8 +104,8 @@ "CreateCSPMAzureAccount", "POST", "/cloud-connect-azure/entities/account/v1", - "Creates a new account in our system for a customer and generates a script for them to " - "run in their cloud environment to grant us access." + "Creates a new account in our system for a customer and generates a script for them to run in " + "their cloud environment to grant us access." ], [ "UpdateCSPMAzureAccountClientID", @@ -137,8 +137,8 @@ "CreateCSPMAwsAccount", "POST", "/cloud-connect-cspm-aws/entities/account/v1", - "Creates a new account in our system for a customer and generates a script for them to run in their " - "AWS cloud environment to grant us access." + "Creates a new account in our system for a customer and generates a script for them to run in " + "their AWS cloud environment to grant us access." ], [ "DeleteCSPMAwsAccount", @@ -202,15 +202,15 @@ "CreateCSPMGCPAccount", "POST", "/cloud-connect-gcp/entities/account/v1", - "Creates a new account in our system for a customer and generates a new service account for them " - "to add access to in their GCP environment to grant us access." + "Creates a new account in our system for a customer and generates a new service account for them to add " + "access to in their GCP environment to grant us access." ], [ "GetCSPMGCPUserScriptsAttachment", "GET", "/cloud-connect-gcp/entities/user-scripts-download/v1", - "Return a script for customer to run in their cloud environment to grant us access to their " - "GCP environment as a downloadable attachment" + "Return a script for customer to run in their cloud environment to grant us access to their GCP environment " + "as a downloadable attachment" ], [ "GetCSPMGCPUserScripts", @@ -338,6 +338,90 @@ "Search for Host Groups in your environment by providing an FQL filter and paging details. " "Returns a set of Host Group IDs which match the filter criteria" ], + [ + "AggregateAllowList", + "POST", + "/falcon-complete-dashboards/aggregates/allowlist/GET/v1", + "Retrieve aggregate allowlist ticket values based on the matched filter" + ], + [ + "AggregateBlockList", + "POST", + "/falcon-complete-dashboards/aggregates/blocklist/GET/v1", + "Retrieve aggregate blocklist ticket values based on the matched filter" + ], + [ + "AggregateDetections", + "POST", + "/falcon-complete-dashboards/aggregates/detects/GET/v1", + "Retrieve aggregate detection values based on the matched filter" + ], + [ + "AggregateDeviceCountCollection", + "POST", + "/falcon-complete-dashboards/aggregates/devicecount-collections/GET/v1", + "Retrieve aggregate host/devices count based on the matched filter" + ], + [ + "AggregateEscalations", + "POST", + "/falcon-complete-dashboards/aggregates/escalations/GET/v1", + "Retrieve aggregate escalation ticket values based on the matched filter" + ], + [ + "AggregateFCIncidents", + "POST", + "/falcon-complete-dashboards/aggregates/incidents/GET/v1", + "Retrieve aggregate incident values based on the matched filter" + ], + [ + "AggregateRemediations", + "POST", + "/falcon-complete-dashboards/aggregates/remediations/GET/v1", + "Retrieve aggregate remediation ticket values based on the matched filter" + ], + [ + "QueryAllowListFilter", + "GET", + "/falcon-complete-dashboards/queries/allowlist/v1", + "Retrieve allowlist tickets that match the provided filter criteria with scrolling enabled" + ], + [ + "QueryBlockListFilter", + "GET", + "/falcon-complete-dashboards/queries/blocklist/v1", + "Retrieve block listtickets that match the provided filter criteria with scrolling enabled" + ], + [ + "QueryDetectionIdsByFilter", + "GET", + "/falcon-complete-dashboards/queries/detects/v1", + "Retrieve DetectionsIds that match the provided FQL filter, criteria with scrolling enabled" + ], + [ + "GetDeviceCountCollectionQueriesByFilter", + "GET", + "/falcon-complete-dashboards/queries/devicecount-collections/v1", + "Retrieve device count collection Ids that match the provided FQL filter, criteria with scrolling enabled" + ], + [ + "QueryEscalationsFilter", + "GET", + "/falcon-complete-dashboards/queries/escalations/v1", + "Retrieve escalation tickets that match the provided filter criteria with scrolling enabled" + ], + [ + "QueryIncidentIdsByFilter", + "GET", + "/falcon-complete-dashboards/queries/incidents/v1", + "Retrieve incidents that match the provided filter criteria with scrolling enabled" + ], + [ + "QueryRemediationsFilter", + "GET", + "/falcon-complete-dashboards/queries/remediations/v1", + "Retrieve remediation tickets that match the provided filter criteria with scrolling enabled" + ], [ "GetArtifacts", "GET", @@ -360,8 +444,8 @@ "DeleteReport", "DELETE", "/falconx/entities/reports/v1?ids={}", - "Delete report based on the report ID. Operation can be checked for success by " - "polling for the report ID on the report-summaries endpoint." + "Delete report based on the report ID. " + "Operation can be checked for success by polling for the report ID on the report-summaries endpoint." ], [ "GetSubmissions", @@ -373,8 +457,8 @@ "Submit", "POST", "/falconx/entities/submissions/v1", - "Submit an uploaded file or a URL for sandbox analysis. Time required for analysis " - "varies but is usually less than 15 minutes." + "Submit an uploaded file or a URL for sandbox analysis. " + "Time required for analysis varies but is usually less than 15 minutes." ], [ "QueryReports", @@ -525,8 +609,8 @@ "PerformIncidentAction", "POST", "/incidents/entities/incident-actions/v1", - "Perform a set of actions on one or more incidents, such as adding tags or " - "comments or updating the incident name or description" + "Perform a set of actions on one or more incidents, " + "such as adding tags or comments or updating the incident name or description" ], [ "GetIncidents", @@ -851,8 +935,9 @@ "PostMalQueryFuzzySearchV1", "POST", "/malquery/combined/fuzzy-search/v1", - "Search Falcon MalQuery quickly, but with more potential for false positives. Search for a combination of " - "hex patterns and strings in order to identify samples based upon file content at byte level granularity." + "Search Falcon MalQuery quickly, but with more potential for false positives. " + "Search for a combination of hex patterns and strings in order to identify samples " + "based upon file content at byte level granularity." ], [ "GetMalQueryDownloadV1", @@ -877,23 +962,23 @@ "GetMalQueryEntitiesSamplesFetchV1", "GET", "/malquery/entities/samples-fetch/v1?ids={}", - "Fetch a zip archive with password 'infected' containing the samples. Call this once the /entities/samples-multidownload " - "request has finished processing" + "Fetch a zip archive with password 'infected' containing the samples. " + "Call this once the /entities/samples-multidownload request has finished processing" ], [ "PostMalQueryEntitiesSamplesMultidownloadV1", "POST", "/malquery/entities/samples-multidownload/v1", - "Schedule samples for download. Use the result id with the /request endpoint to check if the download is ready " - "after which you can call the /entities/samples-fetch to get the zip" + "Schedule samples for download. Use the result id with the /request endpoint to check if the " + "download is ready after which you can call the /entities/samples-fetch to get the zip" ], [ "PostMalQueryExactSearchV1", "POST", "/malquery/queries/exact-search/v1", - "Search Falcon MalQuery for a combination of hex patterns and strings in order to identify samples based " - "upon file content at byte level granularity. You can filter results on criteria such as file type, file size " - "and first seen date. Returns a request id which can be used with the /request endpoint" + "Search Falcon MalQuery for a combination of hex patterns and strings in order to identify samples " + "based upon file content at byte level granularity. You can filter results on criteria such as file type, " + "file size and first seen date. Returns a request id which can be used with the /request endpoint" ], [ "PostMalQueryHuntV1", @@ -901,6 +986,157 @@ "/malquery/queries/hunt/v1", "Schedule a YARA-based search for execution. Returns a request id which can be used with the /request endpoint" ], + [ + "getChildren", + "GET", + "/mssp/entities/children/v1?ids={}", + "Get link to child customer by child CID(s)" + ], + [ + "getCIDGroupMembersBy", + "GET", + "/mssp/entities/cid-group-members/v1", + "Get CID Group members by CID Group IDs." + ], + [ + "addCIDGroupMembers", + "POST", + "/mssp/entities/cid-group-members/v1", + "Add new CID Group member." + ], + [ + "deleteCIDGroupMembers", + "DELETE", + "/mssp/entities/cid-group-members/v1", + "Delete CID Group members entry." + ], + [ + "getCIDGroupById", + "GET", + "/mssp/entities/cid-groups/v1", + "Get CID Group(s) by ID(s)." + ], + [ + "createCIDGroups", + "POST", + "/mssp/entities/cid-groups/v1", + "Create new CID Group(s). Maximum 500 CID Group(s) allowed." + ], + [ + "updateCIDGroups", + "PATCH", + "/mssp/entities/cid-groups/v1", + "Update existing CID Group(s). CID Group ID is expected for each CID Group definition provided in request body. " + "CID Group member(s) remain unaffected." + ], + [ + "deleteCIDGroups", + "DELETE", + "/mssp/entities/cid-groups/v1", + "Delete CID Group(s) by ID(s)." + ], + [ + "getRolesByID", + "GET", + "/mssp/entities/mssp-roles/v1?ids={}", + "Get MSSP Role assignment(s). MSSP Role assignment is of the format :." + ], + [ + "addRole", + "POST", + "/mssp/entities/mssp-roles/v1", + "Assign new MSSP Role(s) between User Group and CID Group. " + "It does not revoke existing role(s) between User Group and CID Group. " + "User Group ID and CID Group ID have to be specified in request. " + ], + [ + "deletedRoles", + "DELETE", + "/mssp/entities/mssp-roles/v1", + "Delete MSSP Role assignment(s) between User Group and CID Group. " + "User Group ID and CID Group ID have to be specified in request. " + "Only specified roles are removed if specified in request payload, " + "else association between User Group and CID Group is dissolved completely (if no roles specified)." + ], + [ + "getUserGroupMembersByID", + "GET", + "/mssp/entities/user-group-members/v1", + "Get User Group members by User Group ID(s)." + ], + [ + "addUserGroupMembers", + "POST", + "/mssp/entities/user-group-members/v1", + "Add new User Group member. Maximum 500 members allowed per User Group." + ], + [ + "deleteUserGroupMembers", + "DELETE", + "/mssp/entities/user-group-members/v1", + "Delete User Group members entry." + ], + [ + "getUserGroupsByID", + "GET", + "/mssp/entities/user-groups/v1", + "Get User Group by ID(s)." + ], + [ + "createUserGroups", + "POST", + "/mssp/entities/user-groups/v1", + "Create new User Group(s). Maximum 500 User Group(s) allowed per customer." + ], + [ + "updateUserGroups", + "PATCH", + "/mssp/entities/user-groups/v1", + "Update existing User Group(s). User Group ID is expected for each User Group definition " + "provided in request body. User Group member(s) remain unaffected." + ], + [ + "deleteUserGroups", + "DELETE", + "/mssp/entities/user-groups/v1", + "Delete User Group(s) by ID(s)." + ], + [ + "queryChildren", + "GET", + "/mssp/queries/children/v1", + "Query for customers linked as children" + ], + [ + "queryCIDGroupMembers", + "GET", + "/mssp/queries/cid-group-members/v1", + "Query a CID Groups members by associated CID." + ], + [ + "queryCIDGroups", + "GET", + "/mssp/queries/cid-groups/v1", + "Query CID Groups." + ], + [ + "queryRoles", + "GET", + "/mssp/queries/mssp-roles/v1", + "Query MSSP Role assignment. At least one of CID Group ID or User Group ID should also be provided. Role ID is optional." + ], + [ + "queryUserGroupMembers", + "GET", + "/mssp/queries/user-group-members/v1", + "Query User Group member by User UUID." + ], + [ + "queryUserGroups", + "GET", + "/mssp/queries/user-groups/v1", + "Query User Groups." + ], [ "oauth2RevokeToken", "POST", @@ -913,6 +1149,36 @@ "/oauth2/token", "Generate an OAuth2 access token" ], + [ + "AggregatesDetectionsGlobalCounts", + "GET", + "/overwatch-dashboards/aggregates/detections-global-counts/v1", + "Get the total number of detections pushed across all customers" + ], + [ + "AggregatesEventsCollections", + "POST", + "/overwatch-dashboards/aggregates/events-collections/GET/v1", + "Get OverWatch detection event collection info by providing an aggregate query" + ], + [ + "AggregatesEvents", + "POST", + "/overwatch-dashboards/aggregates/events/GET/v1", + "Get aggregate OverWatch detection event info by providing an aggregate query" + ], + [ + "AggregatesIncidentsGlobalCounts", + "GET", + "/overwatch-dashboards/aggregates/incidents-global-counts/v1", + "Get the total number of incidents pushed across all customers" + ], + [ + "AggregatesOWEventsGlobalCounts", + "GET", + "/overwatch-dashboards/aggregates/ow-events-global-counts/v1", + "Get the total number of OverWatch events across all customers" + ], [ "queryCombinedDeviceControlPolicyMembers", "GET", @@ -959,8 +1225,8 @@ "revealUninstallToken", "POST", "/policy/combined/reveal-uninstall-token/v1", - "Reveals an uninstall token for a specific device. To retrieve the bulk maintenance token pass the " - "value 'MAINTENANCE' as the value for 'device_id'" + "Reveals an uninstall token for a specific device. To retrieve the bulk maintenance token pass " + "the value 'MAINTENANCE' as the value for 'device_id'" ], [ "queryCombinedSensorUpdateBuilds", @@ -1193,21 +1459,22 @@ "getSensorUpdatePoliciesV2", "GET", "/policy/entities/sensor-update/v2?ids={}", - "Retrieve a set of Sensor Update Policies with additional support for uninstall protection by specifying their IDs" + "Retrieve a set of Sensor Update Policies with additional support for uninstall " + "protection by specifying their IDs" ], [ "createSensorUpdatePoliciesV2", "POST", "/policy/entities/sensor-update/v2", - "Create Sensor Update Policies by specifying details about the policy to create with " - "additional support for uninstall protection" + "Create Sensor Update Policies by specifying details about the policy to create " + "with additional support for uninstall protection" ], [ "updateSensorUpdatePoliciesV2", "PATCH", "/policy/entities/sensor-update/v2", - "Update Sensor Update Policies by specifying the ID of the policy and details to update with " - "additional support for uninstall protection" + "Update Sensor Update Policies by specifying the ID of the policy and details to " + "update with additional support for uninstall protection" ], [ "getSensorVisibilityExclusionsV1", @@ -1291,8 +1558,8 @@ "querySensorUpdatePolicyMembers", "GET", "/policy/queries/sensor-update-members/v1", - "Search for members of a Sensor Update Policy in your environment by providing an FQL filter and paging " - "details. Returns a set of Agent IDs which match the filter criteria" + "Search for members of a Sensor Update Policy in your environment by providing an FQL filter and paging details. " + "Returns a set of Agent IDs which match the filter criteria" ], [ "querySensorUpdatePolicies", @@ -1347,15 +1614,15 @@ "BatchGetCmd", "POST", "/real-time-response/combined/batch-get-command/v1", - "Batch executes `get` command across hosts to retrieve files. After this call is made " - "`GET /real-time-response/combined/batch-get-command/v1` is used to query for the results." + "Batch executes `get` command across hosts to retrieve files. " + "After this call is made `GET /real-time-response/combined/batch-get-command/v1` is used to query for the results." ], [ "BatchInitSessions", "POST", "/real-time-response/combined/batch-init-session/v1", - "Batch initialize a RTR session on multiple hosts. Before any RTR commands can be used, " - "an active session is needed on the host." + "Batch initialize a RTR session on multiple hosts. " + "Before any RTR commands can be used, an active session is needed on the host." ], [ "BatchRefreshSessions", @@ -1573,8 +1840,8 @@ "ScanSamples", "POST", "/scanner/entities/scans/v1", - "Submit a volume of files for ml scanning. Time required for analysis increases with the number " - "of samples in a volume but usually it should take less than 1 minute" + "Submit a volume of files for ml scanning. Time required for analysis increases with the number of " + "samples in a volume but usually it should take less than 1 minute" ], [ "QuerySubmissionsMixin0", @@ -1690,15 +1957,15 @@ "GetAvailableRoleIds", "GET", "/user-roles/queries/user-role-ids-by-cid/v1", - "Show role IDs for all roles available in your customer account. For more information on each role, " - "provide the role ID to `/customer/entities/roles/v1`." + "Show role IDs for all roles available in your customer account. " + "For more information on each role, provide the role ID to `/customer/entities/roles/v1`." ], [ "GetUserRoleIds", "GET", "/user-roles/queries/user-role-ids-by-user-uuid/v1", - "Show role IDs of roles assigned to a user. For more information on each role, " - "provide the role ID to `/customer/entities/roles/v1`." + "Show role IDs of roles assigned to a user. " + "For more information on each role, provide the role ID to `/customer/entities/roles/v1`." ], [ "RetrieveUser", @@ -1734,8 +2001,8 @@ "RetrieveUserUUIDsByCID", "GET", "/users/queries/user-uuids-by-cid/v1", - "List user IDs for all users in your customer account. For more information on each user, " - "provide the user ID to `/users/entities/user/v1`." + "List user IDs for all users in your customer account. " + "For more information on each user, provide the user ID to `/users/entities/user/v1`." ], [ "RetrieveUserUUID", @@ -1743,6 +2010,12 @@ "/users/queries/user-uuids-by-email/v1", "Get a user's ID by providing a username (usually an email address)" ], + [ + "getAssessmentV1", + "GET", + "/zero-trust-assessment/entities/assessments/v1?ids={}", + "Get Zero Trust Assessment data for one or more hosts by providing agent IDs (AID) and a customer ID (CID)." + ], # .---. .----------- # / \ __ / ------ # / / \(..)/ ----- @@ -1755,12 +2028,6 @@ # These operation IDs are maintained for backwards compatibility purposes only, Move all code # references to use the new operations IDs defined above that align with the IDs defined in # the service classes. - [ - "entities.processes", - "GET", - "/processes/entities/processes/v1?ids={}", - "For the provided ProcessID retrieve the process details" - ], [ "aggregate-events", "POST", @@ -1981,7 +2248,7 @@ "get-rulesMixin0", "GET", "/ioarules/entities/rules/v1?ids={}", - "Get rules by ID and optionally version in the following format: `ID[:version]`. " + "Get rules by ID and optionally version in the following format: `ID[:version]`." "The max number of IDs is constrained by URL size." ], [ @@ -2038,6 +2305,12 @@ "/ioarules/queries/rules/v1", "Finds all rule IDs matching the query with optional filter." ], + [ + "entities.processes", + "GET", + "/processes/entities/processes/v1?ids={}", + "For the provided ProcessID retrieve the process details" + ], [ "RTR-AggregateSessions", "POST", From 5d25c40615d5eb76d3111b1c2cab35334c682935 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Sun, 11 Apr 2021 00:47:26 -0400 Subject: [PATCH 05/29] Split out package build testing by operating system --- ...nit_testing.yml => unit_testing_macos.yml} | 5 ++- .github/workflows/unit_testing_ubuntu.yml | 41 +++++++++++++++++++ .github/workflows/unit_testing_windows.yml | 41 +++++++++++++++++++ 3 files changed, 85 insertions(+), 2 deletions(-) rename .github/workflows/{unit_testing.yml => unit_testing_macos.yml} (88%) create mode 100644 .github/workflows/unit_testing_ubuntu.yml create mode 100644 .github/workflows/unit_testing_windows.yml diff --git a/.github/workflows/unit_testing.yml b/.github/workflows/unit_testing_macos.yml similarity index 88% rename from .github/workflows/unit_testing.yml rename to .github/workflows/unit_testing_macos.yml index 4727b2422..21b09d7ec 100644 --- a/.github/workflows/unit_testing.yml +++ b/.github/workflows/unit_testing_macos.yml @@ -17,9 +17,10 @@ jobs: build: strategy: matrix: - os: [macos-latest, windows-latest, ubuntu-latest] + # os: [macos-latest, windows-latest, ubuntu-latest] python-version: ['3.6', '3.7', '3.8', '3.9'] - runs-on: ${{ matrix.os }} + # runs-on: ${{ matrix.os }} + runs-on: macos-latest steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} diff --git a/.github/workflows/unit_testing_ubuntu.yml b/.github/workflows/unit_testing_ubuntu.yml new file mode 100644 index 000000000..077617ff2 --- /dev/null +++ b/.github/workflows/unit_testing_ubuntu.yml @@ -0,0 +1,41 @@ +name: Python package +on: + push: + paths: + - '**.py' + branches: + - main + - 'ver_*' + pull_request: + paths: + - '**.py' + branches: + - main + - 'ver_*' + +jobs: + build: + strategy: + matrix: + # os: [macos-latest, windows-latest, ubuntu-latest] + python-version: ['3.6', '3.7', '3.8', '3.9'] + # runs-on: ${{ matrix.os }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install pytest coverage + pip install -r requirements.txt + - name: Test with pytest + env: + DEBUG_API_ID: ${{ secrets.DEBUG_API_ID }} + DEBUG_API_SECRET: ${{ secrets.DEBUG_API_SECRET }} + run: | + coverage run --source=src --omit=src/falconpy/debug.py -m pytest -s + coverage report diff --git a/.github/workflows/unit_testing_windows.yml b/.github/workflows/unit_testing_windows.yml new file mode 100644 index 000000000..7fa48547c --- /dev/null +++ b/.github/workflows/unit_testing_windows.yml @@ -0,0 +1,41 @@ +name: Python package +on: + push: + paths: + - '**.py' + branches: + - main + - 'ver_*' + pull_request: + paths: + - '**.py' + branches: + - main + - 'ver_*' + +jobs: + build: + strategy: + matrix: + # os: [macos-latest, windows-latest, ubuntu-latest] + python-version: ['3.6', '3.7', '3.8', '3.9'] + # runs-on: ${{ matrix.os }} + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install pytest coverage + pip install -r requirements.txt + - name: Test with pytest + env: + DEBUG_API_ID: ${{ secrets.DEBUG_API_ID }} + DEBUG_API_SECRET: ${{ secrets.DEBUG_API_SECRET }} + run: | + coverage run --source=src --omit=src/falconpy/debug.py -m pytest -s + coverage report From b9c485f4a4ce5bf1dc0e91b35bab9cbc15d6c446 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Sun, 11 Apr 2021 00:48:21 -0400 Subject: [PATCH 06/29] Bump version 0.4.4 -> 0.4.5 --- src/falconpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/falconpy/_version.py b/src/falconpy/_version.py index c207e0364..533da3519 100644 --- a/src/falconpy/_version.py +++ b/src/falconpy/_version.py @@ -36,7 +36,7 @@ For more information, please refer to """ -_version = '0.4.4' +_version = '0.4.5' _maintainer = 'Joshua Hiller' _author = 'CrowdStrike' _author_email = 'falconpy@crowdstrike.com' From 9efee5aceb7c02c99bd7aea7e50e6df40e43f3c6 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Sun, 11 Apr 2021 01:00:59 -0400 Subject: [PATCH 07/29] Documentation updates --- .github/workflows/unit_testing_macos.yml | 2 +- .github/workflows/unit_testing_ubuntu.yml | 2 +- .github/workflows/unit_testing_windows.yml | 2 +- README.md | 12 +++++++----- src/falconpy/README.md | 2 +- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/.github/workflows/unit_testing_macos.yml b/.github/workflows/unit_testing_macos.yml index 21b09d7ec..73cc2430a 100644 --- a/.github/workflows/unit_testing_macos.yml +++ b/.github/workflows/unit_testing_macos.yml @@ -1,4 +1,4 @@ -name: Python package +name: Python package (MacOS) on: push: paths: diff --git a/.github/workflows/unit_testing_ubuntu.yml b/.github/workflows/unit_testing_ubuntu.yml index 077617ff2..c92a1a3a4 100644 --- a/.github/workflows/unit_testing_ubuntu.yml +++ b/.github/workflows/unit_testing_ubuntu.yml @@ -1,4 +1,4 @@ -name: Python package +name: Python package (Ubuntu) on: push: paths: diff --git a/.github/workflows/unit_testing_windows.yml b/.github/workflows/unit_testing_windows.yml index 7fa48547c..b8a26ee8f 100644 --- a/.github/workflows/unit_testing_windows.yml +++ b/.github/workflows/unit_testing_windows.yml @@ -1,4 +1,4 @@ -name: Python package +name: Python package (Windows) on: push: paths: diff --git a/README.md b/README.md index 1d650b1aa..4498eaf7e 100644 --- a/README.md +++ b/README.md @@ -39,13 +39,15 @@ $ python3 -m pip uninstall crowdstrike-falconpy | CrowdStrike Device Control API | [device_control_policies.py](https://github.com/CrowdStrike/falconpy/blob/main/src/falconpy/device_control_policies.py) | | CrowdStrike Falcon Sandbox API | [falconx_sandbox.py](https://github.com/CrowdStrike/falconpy/blob/main/src/falconpy/falconx_sandbox.py) | | CrowdStrike Sensor Policy Management API | [sensor_update_policy.py](https://github.com/CrowdStrike/falconpy/blob/main/src/falconpy/sensor_update_policy.py) | +| CrowdStrike Custom Indicators of Attack (IOAs) APIs | [custom_ioa.py](https://github.com/CrowdStrike/falconpy/blob/main/src/falconpy/custom_ioa.py) | | [CrowdStrike Custom Indicators of Compromise (IOCs) APIs](https://falcon.crowdstrike.com/support/documentation/88/custom-ioc-apis) | [iocs.py](https://github.com/CrowdStrike/falconpy/blob/main/src/falconpy/iocs.py) | | [CrowdStrike Detections APIs](https://falcon.crowdstrike.com/support/documentation/85/detection-and-prevention-policies-apis) | [detects.py](https://github.com/CrowdStrike/falconpy/blob/main/src/falconpy/detects.py) | | [CrowdStrike Event Streams API](https://falcon.crowdstrike.com/support/documentation/89/event-streams-apis)| [event_streams.py](https://github.com/CrowdStrike/falconpy/blob/main/src/falconpy/event_streams.py) | | [CrowdStrike Falcon Horizon APIs](https://falcon.crowdstrike.com/support/documentation/137/falcon-horizon-apis) | [cspm_registration.py](https://github.com/CrowdStrike/falconpy/blob/main/src/falconpy/cspm_registration.py) | -| [CrowdStrike Falcon X APIs](https://falcon.crowdstrike.com/support/documentation/92/falcon-x-apis) | *Coming Soon* | +| [CrowdStrike Falcon X APIs](https://falcon.crowdstrike.com/support/documentation/92/falcon-x-apis) | [sample_uploads.py](https://github.com/CrowdStrike/falconpy/blob/main/src/falconpy/sample_uploads.py) | | [CrowdStrike Firewall Management API](https://falcon.crowdstrike.com/support/documentation/107/falcon-firewall-management-apis) | [firewall_management.py](https://github.com/CrowdStrike/falconpy/blob/main/src/falconpy/firewall_management.py) | | [CrowdStrike Firewall Policy Management](https://falcon.crowdstrike.com/support/documentation/107/falcon-firewall-management-apis) | [firewall_policies.py](https://github.com/CrowdStrike/falconpy/blob/main/src/falconpy/firewall_policies.py) | +| [CrowdStrike Falcon Flight Control APIs](https://falcon.crowdstrike.com/support/documentation/154/flight-control-apis) | *Coming Soon* | | [CrowdStrike Host Groups API](https://falcon.crowdstrike.com/support/documentation/84/host-and-host-group-management-apis) | [host_group.py](https://github.com/CrowdStrike/falconpy/blob/main/src/falconpy/host_group.py) | | [CrowdStrike Hosts API](https://falcon.crowdstrike.com/support/documentation/84/host-and-host-group-management-apis) | [hosts.py](https://github.com/CrowdStrike/falconpy/blob/main/src/falconpy/hosts.py) | | [CrowdStrike Incident and Detection Monitoring APIs](https://falcon.crowdstrike.com/support/documentation/86/detections-monitoring-apis) | [incidents.py](https://github.com/CrowdStrike/falconpy/blob/main/src/falconpy/incidents.py) | @@ -56,13 +58,13 @@ $ python3 -m pip uninstall crowdstrike-falconpy | [CrowdStrike Prevention Policy APIs](https://falcon.crowdstrike.com/support/documentation/85/detection-and-prevention-policies-apis) | [prevention_policy.py](https://github.com/CrowdStrike/falconpy/blob/main/src/falconpy/prevention_policy.py) | | [CrowdStrike Real Time Response (RTR) APIs](https://falcon.crowdstrike.com/support/documentation/90/real-time-response-apis) | [real_time_response.py](https://github.com/CrowdStrike/falconpy/blob/main/src/falconpy/real_time_response.py) | | [CrowdStrike Realtime Response (RTR) Administration API](https://falcon.crowdstrike.com/support/documentation/90/real-time-response-apis) | [real_time_response_admin.py](https://github.com/CrowdStrike/falconpy/blob/main/src/falconpy/real_time_response_admin.py) | -| CrowdStrike Sample Uploads API | [sample_uploads.py](https://github.com/CrowdStrike/falconpy/blob/main/src/falconpy/sample_uploads.py) | | [CrowdStrike Sensor Download APIs](https://falcon.crowdstrike.com/support/documentation/109/sensor-download-apis) | [sensor_download.py](https://github.com/CrowdStrike/falconpy/blob/main/src/falconpy/sensor_download.py) | | [CrowdStrike Spotlight APIs](https://falcon.crowdstrike.com/support/documentation/98/spotlight-apis) | [spotlight_vulnerabilities.py](https://github.com/CrowdStrike/falconpy/blob/main/src/falconpy/spotlight_vulnerabilities.py) | | [CrowdStrike User and Roles API](https://falcon.crowdstrike.com/support/documentation/87/users-and-roles-apis) | [user_management.py](https://github.com/CrowdStrike/falconpy/blob/main/src/falconpy/user_management.py) | | [Falcon Discover for Cloud and Containers - AWS Accounts APIs](https://falcon.crowdstrike.com/support/documentation/91/discover-for-aws-apis) | [cloud_connect_aws.py](https://github.com/CrowdStrike/falconpy/blob/main/src/falconpy/cloud_connect_aws.py) | | [Falcon Discover for Cloud and Containers - Azure Subscriptions APIs](https://falcon.crowdstrike.com/support/documentation/118/falcon-discover-for-cloud-and-containers-azure-subscription-apis) | *Coming Soon* | | [Falcon Discover for Cloud and Containers - GCP Projects APIs](https://falcon.crowdstrike.com/support/documentation/117/falcon-discover-for-cloud-and-containers-gcp-projects-apis) | *Coming Soon* | +| [CrowdStrike Falcon Zero Trust Assessment APIs](https://falcon.crowdstrike.com/support/documentation/156/zero-trust-assessment-apis) | *Coming Soon* | ## Uber class + [api_complete.py](./src/falconpy/api_complete.py) - Provides an interface to all CrowdStrike APIs with a single handler. @@ -70,11 +72,11 @@ $ python3 -m pip uninstall crowdstrike-falconpy # Contributing There are *many* ways you can contribute to the FalconPy project! * ***Providing feedback*** by opening a GitHub ticket. Even a fly-by "Hey, this worked!" is appreciated and helps validate approaches. Ideas on improving the project are most welcome. - * ***Documenting, blogging, or creating videos***, of how you've used FalconPy! This type of content is *invaluable* and helps communities grow. Open a pull request for inclusion in the [Documentation and Collateral](#documentation-and-collateral) section. + * ***Documenting, blogging, or creating videos***, of how you've used FalconPy! This type of content is *invaluable* and helps communities grow. Open a pull request for inclusion in the [Documentation and Collateral](https://github.com/CrowdStrike/falconpy#documentation-and-collateral) section. * ***Fix a bug or implement a new feature***. Check out our [open issues on GitHub](https://github.com/CrowdStrike/falconpy/issues) for inspiration. * ***Review pull requests*** by going through the queue of [open pull requests on GitHub](https://github.com/CrowdStrike/falconpy/pulls) and giving feedback to the authors - > Review [CONTRIBUTING.md](CONTRIBUTING.md) for more details regarding contributing to the FalconPy project. + > Review [CONTRIBUTING.md](https://github.com/CrowdStrike/falconpy/CONTRIBUTING.md) for more details regarding contributing to the FalconPy project. Open to do something else but not sure where to start? Try [opening an issue](https://github.com/CrowdStrike/falconpy/issues/new), or posting a topic in our [discussion board](https://github.com/CrowdStrike/falconpy/discussions), to introduce yourself and your interests. We look forward to chatting with you! @@ -92,7 +94,7 @@ GitHub Discussions provide the community with means to communicate. There are fo * :raised_hands: [**Show and Tell**](https://github.com/CrowdStrike/falconpy/discussions?discussions_q=category%3A%22Show+and+tell%22): Share with the community what you're up to! Perhaps this is letting everyone know about your upcoming conference talk, share a project that has embedded FalconPy, or your recent blog. -# Documentation & Collateral +# Documentation and Collateral ## Official Project Documentation See the wiki for extended documentation: [https://github.com/CrowdStrike/falconpy/wiki](https://github.com/CrowdStrike/falconpy/wiki). diff --git a/src/falconpy/README.md b/src/falconpy/README.md index 67ff5653b..d11302bcc 100644 --- a/src/falconpy/README.md +++ b/src/falconpy/README.md @@ -6,6 +6,7 @@ This folder contains the FalconPy project, a Python 3 interface handler for the ### Currently implemented: + `cloud_connect_aws.py` https://assets.falcon.crowdstrike.com/support/api/swagger.html#/cloud-connect-aws + `cspm-registration.py` https://assets.falcon.crowdstrike.com/support/api/swagger.html#/cspm-registration ++ `custom_ioa.py` https://assets.falcon.crowdstrike.com/support/api/swagger.html#/custom-ioa + `detects.py` https://assets.falcon.crowdstrike.com/support/api/swagger.html#/detects + `device_control_policies.py` https://assets.falcon.crowdstrike.com/support/api/swagger.html#/device-control-policies + `event_streams.py` https://assets.falcon.crowdstrike.com/support/api/swagger.html#/event-streams @@ -30,7 +31,6 @@ This folder contains the FalconPy project, a Python 3 interface handler for the ### Planned + `d4c_registration.py` https://assets.falcon.crowdstrike.com/support/api/swagger.html#/d4c-registration + `installation_tokens.py` https://assets.falcon.crowdstrike.com/support/api/swagger.html#/installation-tokens -+ `custom_ioa.py` https://assets.falcon.crowdstrike.com/support/api/swagger.html#/custom-ioa + `malquery.py` https://assets.falcon.crowdstrike.com/support/api/swagger.html#/malquery + `ioa_exclusions.py` https://assets.falcon.crowdstrike.com/support/api/swagger.html#/ioa-exclusions + `ml_exclusions.py` https://assets.falcon.crowdstrike.com/support/api/swagger.html#/ml-exclusions From 1464a28889c9928f33feb81a3d5648adbc708625 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Sun, 11 Apr 2021 01:03:44 -0400 Subject: [PATCH 08/29] Documentation updates --- src/falconpy/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/falconpy/README.md b/src/falconpy/README.md index d11302bcc..51b0662ab 100644 --- a/src/falconpy/README.md +++ b/src/falconpy/README.md @@ -31,6 +31,7 @@ This folder contains the FalconPy project, a Python 3 interface handler for the ### Planned + `d4c_registration.py` https://assets.falcon.crowdstrike.com/support/api/swagger.html#/d4c-registration + `installation_tokens.py` https://assets.falcon.crowdstrike.com/support/api/swagger.html#/installation-tokens ++ `mssp.py` https://assets.falcon.crowdstrike.com/support/api/swagger.html#/mssp + `malquery.py` https://assets.falcon.crowdstrike.com/support/api/swagger.html#/malquery + `ioa_exclusions.py` https://assets.falcon.crowdstrike.com/support/api/swagger.html#/ioa-exclusions + `ml_exclusions.py` https://assets.falcon.crowdstrike.com/support/api/swagger.html#/ml-exclusions From a7c355b3cd159055f970deae35d55ef268a9d4e5 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Sun, 11 Apr 2021 01:11:00 -0400 Subject: [PATCH 09/29] Comment updates --- src/falconpy/sensor_download.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/falconpy/sensor_download.py b/src/falconpy/sensor_download.py index 3b7b42b47..d14bce7fc 100644 --- a/src/falconpy/sensor_download.py +++ b/src/falconpy/sensor_download.py @@ -8,7 +8,7 @@ class Sensor_Download(ServiceClass): def GetCombinedSensorInstallersByQuery(self: object, parameters: dict = {}) -> dict: """ - retrieve all metadata for installers from provided query + Retrieve all metadata for installers from provided query """ FULL_URL = self.base_url+'/sensors/combined/installers/v1' HEADERS = self.headers @@ -28,8 +28,8 @@ def DownloadSensorInstallerById(self: object, download_path: str = None ) -> object: """ - download the sensor by the sha256 into the specified directory. - the path will be created for the user if it does not already exist + Download the sensor by the sha256 id, into the specified directory. + The path will be created for the user if it does not already exist """ FULL_URL = self.base_url+"/sensors/entities/download-installer/v1" HEADERS = self.headers @@ -67,7 +67,7 @@ def GetSensorInstallersEntities(self: object, ids: list or str) -> object: def GetSensorInstallersCCIDByQuery(self: object) -> dict: """ - retrieve the CID for the current oauth environment + Retrieve the CID for the current oauth environment """ FULL_URL = self.base_url+'/sensors/queries/installers/ccid/v1' HEADERS = self.headers @@ -81,7 +81,7 @@ def GetSensorInstallersCCIDByQuery(self: object) -> dict: def GetSensorInstallersByQuery(self: object, parameters: dict = {}) -> dict: """ - retrieve a list of SHA256 for installers based on the filter + Retrieve a list of SHA256 for installers based on the filter """ FULL_URL = self.base_url+'/sensors/queries/installers/v1' HEADERS = self.headers From 7888f6c7160692d21297d71ca86ff51a6ad66dfb Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Sun, 11 Apr 2021 01:42:37 -0400 Subject: [PATCH 10/29] Added Quick Scan service class --- README.md | 2 +- src/falconpy/README.md | 2 +- src/falconpy/quick_scan.py | 108 +++++++++++++++++++++++++++++++++++++ tests/test_custom_ioa.py | 2 +- tests/test_quick_scan.py | 43 +++++++++++++++ 5 files changed, 154 insertions(+), 3 deletions(-) create mode 100644 src/falconpy/quick_scan.py create mode 100644 tests/test_quick_scan.py diff --git a/README.md b/README.md index 4498eaf7e..0ee5903c3 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ $ python3 -m pip uninstall crowdstrike-falconpy | [CrowdStrike Detections APIs](https://falcon.crowdstrike.com/support/documentation/85/detection-and-prevention-policies-apis) | [detects.py](https://github.com/CrowdStrike/falconpy/blob/main/src/falconpy/detects.py) | | [CrowdStrike Event Streams API](https://falcon.crowdstrike.com/support/documentation/89/event-streams-apis)| [event_streams.py](https://github.com/CrowdStrike/falconpy/blob/main/src/falconpy/event_streams.py) | | [CrowdStrike Falcon Horizon APIs](https://falcon.crowdstrike.com/support/documentation/137/falcon-horizon-apis) | [cspm_registration.py](https://github.com/CrowdStrike/falconpy/blob/main/src/falconpy/cspm_registration.py) | -| [CrowdStrike Falcon X APIs](https://falcon.crowdstrike.com/support/documentation/92/falcon-x-apis) | [sample_uploads.py](https://github.com/CrowdStrike/falconpy/blob/main/src/falconpy/sample_uploads.py) | +| [CrowdStrike Falcon X APIs](https://falcon.crowdstrike.com/support/documentation/92/falcon-x-apis) | [sample_uploads.py](https://github.com/CrowdStrike/falconpy/blob/main/src/falconpy/sample_uploads.py)
[quick_scan.py](https://github.com/CrowdStrike/falconpy/blob/main/src/falconpy/quick_scan.py)| | [CrowdStrike Firewall Management API](https://falcon.crowdstrike.com/support/documentation/107/falcon-firewall-management-apis) | [firewall_management.py](https://github.com/CrowdStrike/falconpy/blob/main/src/falconpy/firewall_management.py) | | [CrowdStrike Firewall Policy Management](https://falcon.crowdstrike.com/support/documentation/107/falcon-firewall-management-apis) | [firewall_policies.py](https://github.com/CrowdStrike/falconpy/blob/main/src/falconpy/firewall_policies.py) | | [CrowdStrike Falcon Flight Control APIs](https://falcon.crowdstrike.com/support/documentation/154/flight-control-apis) | *Coming Soon* | diff --git a/src/falconpy/README.md b/src/falconpy/README.md index 51b0662ab..456fe0d19 100644 --- a/src/falconpy/README.md +++ b/src/falconpy/README.md @@ -20,6 +20,7 @@ This folder contains the FalconPy project, a Python 3 interface handler for the + `iocs.py` https://assets.falcon.crowdstrike.com/support/api/swagger.html#/iocs + `oauth2.py` https://assets.falcon.crowdstrike.com/support/api/swagger.html#/oauth2 + `prevention_policy.py` https://assets.falcon.crowdstrike.com/support/api/swagger.html#/prevention-policies ++ `quick_scan.py` https://assets.falcon.crowdstrike.com/support/api/swagger.html#/quick-scan + `real_time_response_admin.py` https://assets.falcon.crowdstrike.com/support/api/swagger.html#/real-time-response-admin + `real_time_response.py` https://assets.falcon.crowdstrike.com/support/api/swagger.html#/real-time-response + `sample_uploads.py` https://assets.falcon.crowdstrike.com/support/api/swagger.html#/sample-uploads @@ -36,7 +37,6 @@ This folder contains the FalconPy project, a Python 3 interface handler for the + `ioa_exclusions.py` https://assets.falcon.crowdstrike.com/support/api/swagger.html#/ioa-exclusions + `ml_exclusions.py` https://assets.falcon.crowdstrike.com/support/api/swagger.html#/ml-exclusions + `sensor_visibility_exclusions.py` https://assets.falcon.crowdstrike.com/support/api/swagger.html#/sensor-visibility-exclusions -+ `quick_scan.py` https://assets.falcon.crowdstrike.com/support/api/swagger.html#/quick-scan ## The Uber Class ### A single class to interface with the entire API diff --git a/src/falconpy/quick_scan.py b/src/falconpy/quick_scan.py new file mode 100644 index 000000000..982859f3f --- /dev/null +++ b/src/falconpy/quick_scan.py @@ -0,0 +1,108 @@ +""" + _______ __ _______ __ __ __ +| _ .----.-----.--.--.--.--| | _ | |_.----|__| |--.-----. +|. 1___| _| _ | | | | _ | 1___| _| _| | <| -__| +|. |___|__| |_____|________|_____|____ |____|__| |__|__|__|_____| +|: 1 | |: 1 | +|::.. . | CROWDSTRIKE FALCON |::.. . | FalconPy +`-------' `-------' + +OAuth2 API - Customer SDK + +quick_scan - Falcon Quick Scan API Interface Class + +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 +""" +from ._util import service_request, parse_id_list +from ._service_class import ServiceClass + + +class Quick_Scan(ServiceClass): + """The only requirement to instantiate an instance of this class + is a valid token provided by the Falcon API SDK OAuth2 class. + """ + def GetScansAggregates(self: object, body: dict) -> dict: + """Get scans aggregations as specified via json in request body.""" + # [POST] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/quick-scan/GetScansAggregates + FULL_URL = self.base_url+"/scanner/aggregates/scans/GET/v1" + HEADERS = self.headers + BODY = body + returned = service_request(caller=self, + method="POST", + endpoint=FULL_URL, + body=BODY, + headers=HEADERS, + verify=self.ssl_verify + ) + return returned + + def GetScans(self: object, ids) -> dict: + """Check the status of a volume scan. Time required for analysis increases with the number + of samples in a volume but usually it should take less than 1 minute + """ + # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/quick-scan/GetScans + ID_LIST = str(parse_id_list(ids)).replace(",", "&ids=") + FULL_URL = self.base_url+"/scanner/entities/scans/v1?ids={}".format(ID_LIST) + HEADERS = self.headers + returned = service_request(caller=self, + method="GET", + endpoint=FULL_URL, + headers=HEADERS, + verify=self.ssl_verify + ) + return returned + + def ScanSamples(self: object, body: dict) -> dict: + """Get scans aggregations as specified via json in request body.""" + # [POST] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/quick-scan/ScanSamples + FULL_URL = self.base_url+"/scanner/entities/scans/v1" + HEADERS = self.headers + BODY = body + returned = service_request(caller=self, + method="POST", + endpoint=FULL_URL, + body=BODY, + headers=HEADERS, + verify=self.ssl_verify + ) + return returned + + def QuerySubmissionsMixin0(self: object, parameters: dict = {}) -> dict: + """Find IDs for submitted scans by providing an FQL filter and paging details. + Returns a set of volume IDs that match your criteria. + """ + # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/quick-scan/QuerySubmissionsMixin0 + FULL_URL = self.base_url+"/scanner/queries/scans/v1" + HEADERS = self.headers + PARAMS = parameters + returned = service_request(caller=self, + method="GET", + endpoint=FULL_URL, + headers=HEADERS, + params=PARAMS, + verify=self.ssl_verify + ) + return returned diff --git a/tests/test_custom_ioa.py b/tests/test_custom_ioa.py index 19580d47a..85733042f 100644 --- a/tests/test_custom_ioa.py +++ b/tests/test_custom_ioa.py @@ -20,7 +20,7 @@ AllowedResponses = [200, 201, 207, 429] # Adding rate-limiting as an allowed response for now -class TestCSPMRegistration: +class TestCustomIOA: def serviceIOA_QueryPatterns(self): return falcon.query_patterns()["status_code"] in AllowedResponses diff --git a/tests/test_quick_scan.py b/tests/test_quick_scan.py new file mode 100644 index 000000000..b8b58b58d --- /dev/null +++ b/tests/test_quick_scan.py @@ -0,0 +1,43 @@ +# test_quick_scam.py +# This class tests the quick_scan service class + +# import json +import os +import sys + +# Authentication via the test_authorization.py +from tests import test_authorization as Authorization + +# Import our sibling src folder into the path +sys.path.append(os.path.abspath('src')) +# Classes to test - manually imported from sibling folder +from falconpy import quick_scan as FalconScan + +auth = Authorization.TestAuthorization() +auth.serviceAuth() +falcon = FalconScan.Quick_Scan(access_token=auth.token) +AllowedResponses = [200, 201, 207, 429] # Adding rate-limiting as an allowed response for now + + +class TestQuickScan: + + def serviceScan_GenerateErrors(self): + falcon.base_url = "nowhere" + errorChecks = True + commandList = [ + ["GetScansAggregates", "body={}"], + ["GetScans", "ids='12345678'"], + ["ScanSamples", "body={}"], + ["QuerySubmissionsMixin0", ""] + ] + for cmd in commandList: + if eval("falcon.{}({})['status_code']".format(cmd[0], cmd[1])) != 500: + errorChecks = False + + return errorChecks + + def test_Logout(self): + assert auth.serviceRevoke() is True + + def test_Errors(self): + assert self.serviceScan_GenerateErrors() is True From 4cf51baf1b297cdc96a1dfcaba85e1d718e34bbf Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Sun, 11 Apr 2021 02:08:52 -0400 Subject: [PATCH 11/29] Create CHANGELOG --- CHANGELOG | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 CHANGELOG diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 000000000..cbfc7eab5 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,160 @@ +# Version 0.4.5 +## Added features and functionality ++ Added: Custom Indicators of Attack (IOA) API Service Class + * get_patterns + * get_platformsMixin0 + * get_rule_groupsMixin0 + * create_rule_groupMixin0 + * delete_rule_groupsMixin0 + * update_rule_groupMixin0 + * get_rule_types + * get_rules_get + * get_rulesMixin0 + * create_rule + * delete_rules + * update_rules + * validate + * query_patterns + * query_platformsMixin0 + * query_rule_groups_fulle + * query_rule_groupsMixin0 + * query_rule_types + * query_rulesMixin0 + - Added unit tests + ++ Added: Falcon X Quick Scan API Service Class + * GetScansAggregate + * GetScans + * ScanSamples + * QuerySubmissionsMixin0 + ++ Added: Uber class endpoints + * Falcon Complete Dashboard API + * Falcon Overwatch Dashboard API + * Falcon Flight Control API + +## Other ++ Added CHANGELOG ++ Documentation updates to reflect new service class and upcoming API additions ++ Minor comment updates + +# Version 0.4.4 +## Added features and functionality ++ Added: Sensor Download API Service Class (Contributor: CalebSchwartz) + * GetCombinedSensorInstallersByQuery + * DownloadSensorInstallerById + * GetSensorInstallersEntities + * GetSensorInstallersCCIDByQuery + * GetSensorInstallersByQuery + - Added unit tests +## Issues resolved ++ Fixed: action_name parameter default bug. Resolved by setting a default value and overriding this value if action_name is present in the parameters dictionary, Closes #114. +## Other ++ Documentation updated to reflect the new Sensor Download Service Class + + +# Version 0.4.3 +## Added features and functionality ++ Added: Sample_Uploads service class (sample_uploads.py) + * UploadSampleV3 + * GetSampleV3 + * DeleteSampleV3 + - Added: Sample_Uploads unit tests (test_sample_uploads.py) ++ Added: FalconDebug - Interactive Python3 debugger that provides a pre-defined API token. +## Issues resolved ++ Fixed: Issue with Uber class command method using the action_name variable instead of file_name variable for actions passing the file_name parameter. ++ Fixed: Issue with setup.py passing GitHub emoji text to the package description. ++ Fixed: Issue with Uber class unit testing not deleting uploaded files from Sample_Uploads API. (test_uber_api_complete.py) + + +# Version 0.4.2 +## Added features and functionality ++ Added missing method: hosts.py - Added UpdateDeviceTags method to Hosts service class. (Contributor: rewgord) + - Unit test added to test_hosts.py to test device tagging functionality. ++ API Operation summaries added to the Uber class: _endpoint.py - This provides for upcoming functionality that will be announced in future updates. ++ New endpoints added to the Uber class: _endpoint.py + +> Deprecation Warning: Legacy API operation IDs that made use of the Python reserved characters "." and "-" have been deprecated. +> New operation IDs have been generated for each that now aligns to the method names defined in the equivalent service class. + +## Issues resolved ++ Added method validation to Uber class calls to the requests library. (HTTP 418 is sent when an invalid method is specified.) +## Other ++ Cleaned up event_streams.py class file to match new patterns. ++ Updated return type decorators for service_request and perform_request. (_util.py) ++ Updated return type decorators for GetArtifacts, GetReports and GetSampleV2. (falconx_sandbox.py) ++ Abstracted all remaining common error output code paths to a stand-alone generic method. (_util.py) + + +# Version 0.4.1 +## Added features and functionality ++ New service class: cspm_registration.py - Provides the *CSPM_Registration* service class for handling Horizon registration in Azure and AWS. + - Unit test added ++ Added methods: falconx_sandbox.py - Support for the following operations have been added to the *FalconX_Sandbox* service class. + * QuerySampleV1 + * DeleteSampleV2 + * GetSampleV2 + * DeleteReport + * GetReports + - Unit test added +## Issues resolved ++ Bug fix: Resolved malformed validator in detects.py - UpdateDetectsByIdsV2 ++ Bug fix: Added action_name parameter to operations that require the parameter. (#53) + This issue impacted 6 service classes in total: + - device_control_policies.py - *Device_Control_Policies* - performDeviceControlPoliciesAction + - firewall_policies.py - *Firewall_Policies* - performFirewallPoliciesAction + - host_group.py - *Host_Group* - performGroupAction + - hosts.py - *Host* - PerformActionV2 + - prevention_policy.py - *Prevention_Policy* - performPreventionPoliciesAction + - sensor_update_policy.py - *Sensor_Update_Policy* - performSensorUpdatePoliciesAction + + - This issue also impacted the Uber class, resulting in updates to the command method within the APIHarness class. + + - Unit tests modified + +> Breaking Change: The action_name parameter does not currently accept unspecified values. This is resolved in the 0.4.4 version of the package. + +## Other ++ Minor updates to _endpoints.py to reflect operation ID corrections for the CSPM registration API. ++ Abstracted common error output code paths to a stand-alone method within _util.py. + + +# Version 0.4.0 +## Added features and functionality ++ Added additional HTTP status codes ++ Added parameter input validation handling + - Additional validations are planned for all service classes. Currently only enabled in cloud_connect_aws.py. ++ Added body payload input validation handling + - Additional validations are planned for all service classes. Currently only enabled in cloud_connect_aws.py. ++ Added allowed HTTP method restrictions ++ Added ID list handling to API operations that require ID lists + - Developers may now pass in a list of IDs or a comma-delimited string. ++ Added status code response checks to authentication events ++ Instantiate Service classes without having to manage tokens + - Pass in credentials (Now referred to as "credential authentication") + - Pass in the entire auth object (Now referred to as "object authentication") + > Please note: Passing a token into Service classes is still fully supported. This is now referred to as "legacy authentication". ++ Added automatic token refresh functionality to Service Class calls + - Developers must make use of either credential or object authentication in order to leverage this functionality. +## Issues resolved ++ Added dynamic package metadata updates (Issue #14) + - Generalized version control + - New constant file: `_version.py` ++ Added user-agent string to HTTP headers. (Issue #57) ++ Resolved a bug with token deauthentication (Uber and Service classes) ++ Resolved a bug in Firewall_Management.update_rule_group +## Other ++ Abstracted calls to the requests library from all classes, reducing code segment size + - New library: _util.py + - New class: _service_class.py + - New class: _result.py + - All Service Classes refactored ++ Abstracted endpoint list from the Uber class to a standalone source file + - New constant file: _endpoint.py ++ Linting / code cleanup + - Added function input parameter datatype specifications (where possible) + - Added function output datatype decorators + - In order to reduce confusion, references to the json requests attribute are now always referred to as "body". + - References to the data requests attribute are still referred to as "data". ++ 100% unit test coverage ++ Internal documentation updates \ No newline at end of file From 4c9304c14872d3cce7005d50446534ffbbc7d747 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Sun, 11 Apr 2021 02:10:31 -0400 Subject: [PATCH 12/29] Update CHANGELOG --- CHANGELOG => CHANGELOG.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename CHANGELOG => CHANGELOG.md (100%) diff --git a/CHANGELOG b/CHANGELOG.md similarity index 100% rename from CHANGELOG rename to CHANGELOG.md From 92e33985547aab0d171ec1ff04dc9777d08250df Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Sun, 11 Apr 2021 02:16:43 -0400 Subject: [PATCH 13/29] Update CHANGELOG --- CHANGELOG.md | 47 ++++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cbfc7eab5..6a1630efc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Version 0.4.5 ## Added features and functionality -+ Added: Custom Indicators of Attack (IOA) API Service Class ++ Added: Custom Indicators of Attack (IOA) API Service Class (`custom_ioa.py`) * get_patterns * get_platformsMixin0 * get_rule_groupsMixin0 @@ -20,15 +20,16 @@ * query_rule_groupsMixin0 * query_rule_types * query_rulesMixin0 - - Added unit tests + - Added unit tests (`test_custom_ioa.py`) -+ Added: Falcon X Quick Scan API Service Class ++ Added: Falcon X Quick Scan API Service Class (`quick_scan.py`) * GetScansAggregate * GetScans * ScanSamples * QuerySubmissionsMixin0 + - Added unit tests (`test_quick_scan.py`) -+ Added: Uber class endpoints ++ Added: Uber class endpoints (`_endpoints.py`) * Falcon Complete Dashboard API * Falcon Overwatch Dashboard API * Falcon Flight Control API @@ -40,7 +41,7 @@ # Version 0.4.4 ## Added features and functionality -+ Added: Sensor Download API Service Class (Contributor: CalebSchwartz) ++ Added: Sensor Download API Service Class (Contributor: @CalebSchwartz) * GetCombinedSensorInstallersByQuery * DownloadSensorInstallerById * GetSensorInstallersEntities @@ -55,24 +56,24 @@ # Version 0.4.3 ## Added features and functionality -+ Added: Sample_Uploads service class (sample_uploads.py) ++ Added: Sample_Uploads service class (`sample_uploads.py`) * UploadSampleV3 * GetSampleV3 * DeleteSampleV3 - - Added: Sample_Uploads unit tests (test_sample_uploads.py) + - Added: Sample_Uploads unit tests (`test_sample_uploads.py`) + Added: FalconDebug - Interactive Python3 debugger that provides a pre-defined API token. ## Issues resolved + Fixed: Issue with Uber class command method using the action_name variable instead of file_name variable for actions passing the file_name parameter. -+ Fixed: Issue with setup.py passing GitHub emoji text to the package description. -+ Fixed: Issue with Uber class unit testing not deleting uploaded files from Sample_Uploads API. (test_uber_api_complete.py) ++ Fixed: Issue with `setup.py` passing GitHub emoji text to the package description. ++ Fixed: Issue with Uber class unit testing not deleting uploaded files from Sample_Uploads API. (`test_uber_api_complete.py`) # Version 0.4.2 ## Added features and functionality -+ Added missing method: hosts.py - Added UpdateDeviceTags method to Hosts service class. (Contributor: rewgord) - - Unit test added to test_hosts.py to test device tagging functionality. -+ API Operation summaries added to the Uber class: _endpoint.py - This provides for upcoming functionality that will be announced in future updates. -+ New endpoints added to the Uber class: _endpoint.py ++ Added missing method: `hosts.py` - Added UpdateDeviceTags method to Hosts service class. (Contributor: @rewgord) + - Unit test added to `test_hosts.py` to test device tagging functionality. ++ API Operation summaries added to the Uber class: `_endpoint.py` - This provides for upcoming functionality that will be announced in future updates. ++ New endpoints added to the Uber class: `_endpoint.py` > Deprecation Warning: Legacy API operation IDs that made use of the Python reserved characters "." and "-" have been deprecated. > New operation IDs have been generated for each that now aligns to the method names defined in the equivalent service class. @@ -81,9 +82,9 @@ + Added method validation to Uber class calls to the requests library. (HTTP 418 is sent when an invalid method is specified.) ## Other + Cleaned up event_streams.py class file to match new patterns. -+ Updated return type decorators for service_request and perform_request. (_util.py) -+ Updated return type decorators for GetArtifacts, GetReports and GetSampleV2. (falconx_sandbox.py) -+ Abstracted all remaining common error output code paths to a stand-alone generic method. (_util.py) ++ Updated return type decorators for service_request and perform_request. (`_util.py`) ++ Updated return type decorators for GetArtifacts, GetReports and GetSampleV2. (`falconx_sandbox.py`) ++ Abstracted all remaining common error output code paths to a stand-alone generic method. (`_util.py`) # Version 0.4.1 @@ -99,7 +100,7 @@ - Unit test added ## Issues resolved + Bug fix: Resolved malformed validator in detects.py - UpdateDetectsByIdsV2 -+ Bug fix: Added action_name parameter to operations that require the parameter. (#53) ++ Bug fix: Added action_name parameter to operations that require the parameter. Closes #53. This issue impacted 6 service classes in total: - device_control_policies.py - *Device_Control_Policies* - performDeviceControlPoliciesAction - firewall_policies.py - *Firewall_Policies* - performFirewallPoliciesAction @@ -115,17 +116,17 @@ > Breaking Change: The action_name parameter does not currently accept unspecified values. This is resolved in the 0.4.4 version of the package. ## Other -+ Minor updates to _endpoints.py to reflect operation ID corrections for the CSPM registration API. -+ Abstracted common error output code paths to a stand-alone method within _util.py. ++ Minor updates to `_endpoints.py` to reflect operation ID corrections for the CSPM registration API. ++ Abstracted common error output code paths to a stand-alone method within `_util.py`. # Version 0.4.0 ## Added features and functionality + Added additional HTTP status codes + Added parameter input validation handling - - Additional validations are planned for all service classes. Currently only enabled in cloud_connect_aws.py. + - Additional validations are planned for all service classes. Currently only enabled in `cloud_connect_aws.py`. + Added body payload input validation handling - - Additional validations are planned for all service classes. Currently only enabled in cloud_connect_aws.py. + - Additional validations are planned for all service classes. Currently only enabled in `cloud_connect_aws.py`. + Added allowed HTTP method restrictions + Added ID list handling to API operations that require ID lists - Developers may now pass in a list of IDs or a comma-delimited string. @@ -154,7 +155,7 @@ + Linting / code cleanup - Added function input parameter datatype specifications (where possible) - Added function output datatype decorators - - In order to reduce confusion, references to the json requests attribute are now always referred to as "body". - - References to the data requests attribute are still referred to as "data". + - In order to reduce confusion, references to the `json` requests attribute are now always referred to as "body". + - References to the `data` requests attribute are still referred to as "data". + 100% unit test coverage + Internal documentation updates \ No newline at end of file From c59db2ef4f4911d827e6945ff78269c175744236 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Sun, 11 Apr 2021 02:18:46 -0400 Subject: [PATCH 14/29] Update CHANGELOG --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a1630efc..ee8765ec7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -81,7 +81,7 @@ ## Issues resolved + Added method validation to Uber class calls to the requests library. (HTTP 418 is sent when an invalid method is specified.) ## Other -+ Cleaned up event_streams.py class file to match new patterns. ++ Cleaned up `event_streams.py` class file to match new patterns. + Updated return type decorators for service_request and perform_request. (`_util.py`) + Updated return type decorators for GetArtifacts, GetReports and GetSampleV2. (`falconx_sandbox.py`) + Abstracted all remaining common error output code paths to a stand-alone generic method. (`_util.py`) @@ -138,10 +138,10 @@ + Added automatic token refresh functionality to Service Class calls - Developers must make use of either credential or object authentication in order to leverage this functionality. ## Issues resolved -+ Added dynamic package metadata updates (Issue #14) ++ Added dynamic package metadata updates (Closes #14) - Generalized version control - New constant file: `_version.py` -+ Added user-agent string to HTTP headers. (Issue #57) ++ Added user-agent string to HTTP headers. (Closes #57) + Resolved a bug with token deauthentication (Uber and Service classes) + Resolved a bug in Firewall_Management.update_rule_group ## Other From f8a363f1af5db1b761d60df0580a4995811afcfa Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Sun, 11 Apr 2021 02:22:40 -0400 Subject: [PATCH 15/29] Workflow adjustments --- .github/workflows/dev-deploy.yml | 2 +- .github/workflows/python-publish.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dev-deploy.yml b/.github/workflows/dev-deploy.yml index 1ef502b07..095f88774 100644 --- a/.github/workflows/dev-deploy.yml +++ b/.github/workflows/dev-deploy.yml @@ -1,4 +1,4 @@ -name: Test Package Build and Deploy +name: Publish Test Python Package on: push: diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 5305b9e09..3d9397458 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -1,7 +1,7 @@ # This workflow will upload a Python Package using Twine when a release is created # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries -name: Upload Python Package +name: Publish Python Package on: release: From a906937b39d1305fdd3ed23dea17d71a42598754 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Sun, 11 Apr 2021 02:37:33 -0400 Subject: [PATCH 16/29] Update CHANGELOG --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee8765ec7..58951579f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,14 +16,14 @@ * validate * query_patterns * query_platformsMixin0 - * query_rule_groups_fulle + * query_rule_groups_full * query_rule_groupsMixin0 * query_rule_types * query_rulesMixin0 - Added unit tests (`test_custom_ioa.py`) + Added: Falcon X Quick Scan API Service Class (`quick_scan.py`) - * GetScansAggregate + * GetScansAggregates * GetScans * ScanSamples * QuerySubmissionsMixin0 @@ -38,6 +38,8 @@ + Added CHANGELOG + Documentation updates to reflect new service class and upcoming API additions + Minor comment updates ++ Adjusted GitHub actions to test operating systems as separate workflows ++ Minor GitHub workflow adjustments # Version 0.4.4 ## Added features and functionality From a9ed3fa83ff0a467261b835b650dbc716fc2fffa Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Sun, 11 Apr 2021 02:37:43 -0400 Subject: [PATCH 17/29] Wordlist updates --- .github/wordlist.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/wordlist.txt b/.github/wordlist.txt index c14c785d5..957711a07 100644 --- a/.github/wordlist.txt +++ b/.github/wordlist.txt @@ -2,6 +2,9 @@ APIs CrowdScore CrowdStrike CrowdStrike's +Overwatch +APIHarness +deauthentication detections initialisation SDK @@ -58,6 +61,7 @@ platformsMixin groupMixin severities IOA +IOAs IOC ioa ioc @@ -381,3 +385,9 @@ Unlicense errored XZY toc +performFirewallPoliciesAction +mssp +CalebSchwartz +rewgord +validator +UpdateDeviceTags \ No newline at end of file From d161b9d95cc94021e175b2bbdce45665d6dd2a00 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Sun, 11 Apr 2021 03:17:04 -0400 Subject: [PATCH 18/29] Documentation updates --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0ee5903c3..8a42eb655 100644 --- a/README.md +++ b/README.md @@ -66,8 +66,9 @@ $ python3 -m pip uninstall crowdstrike-falconpy | [Falcon Discover for Cloud and Containers - GCP Projects APIs](https://falcon.crowdstrike.com/support/documentation/117/falcon-discover-for-cloud-and-containers-gcp-projects-apis) | *Coming Soon* | | [CrowdStrike Falcon Zero Trust Assessment APIs](https://falcon.crowdstrike.com/support/documentation/156/zero-trust-assessment-apis) | *Coming Soon* | -## Uber class -+ [api_complete.py](./src/falconpy/api_complete.py) - Provides an interface to all CrowdStrike APIs with a single handler. +# The Uber class ++ [api_complete.py](./src/falconpy/api_complete.py) - The Uber class provides an interface to all CrowdStrike APIs with a single handler. +This soluion supports communicating with API endpoints that do not have an available Service Class or are recently released. # Contributing There are *many* ways you can contribute to the FalconPy project! From 6452f440e9809d8d26c3fd5a76b0ed39b03127d2 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Sun, 11 Apr 2021 03:19:10 -0400 Subject: [PATCH 19/29] Fixed typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8a42eb655..00d25de13 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ $ python3 -m pip uninstall crowdstrike-falconpy # The Uber class + [api_complete.py](./src/falconpy/api_complete.py) - The Uber class provides an interface to all CrowdStrike APIs with a single handler. -This soluion supports communicating with API endpoints that do not have an available Service Class or are recently released. +This solution supports communicating with API endpoints that do not have an available Service Class or are recently released. # Contributing There are *many* ways you can contribute to the FalconPy project! From 6451c204ea64881c073ceb5a412193fbb0734079 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Sun, 11 Apr 2021 18:11:51 -0400 Subject: [PATCH 20/29] Documentation updates --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 00d25de13..eedf4170d 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,8 @@ Stable releases of FalconPy are available on the Python Package Index: $ python3 -m pip install crowdstrike-falconpy ``` -If you'd like to try the *absolute bleeding edge*, an automated GitHub action releases a test package with every merged pull request. To install the testing version: +If you'd like to try the *absolute bleeding edge*, an automated GitHub action releases a test package with every merged pull request containing the string +`[DEPLOY]` in the head of the commit. To install the testing version: ```shell $ python3 -m pip install -i https://test.pypi.org/simple crowdstrike-falconpy ``` From 5aae6fb88d6e9a95cd41ee56a30903f877204efc Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Sun, 11 Apr 2021 18:12:23 -0400 Subject: [PATCH 21/29] Documentation updates --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eedf4170d..82b0c7ac0 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ If you'd like to try the *absolute bleeding edge*, an automated GitHub action re $ python3 -m pip install -i https://test.pypi.org/simple crowdstrike-falconpy ``` -To uninstall/remove FalconPy: +To uninstall and remove FalconPy: ```shell $ python3 -m pip uninstall crowdstrike-falconpy ``` From a1d411a585d7aefeb049d084820c393302395e47 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Sun, 11 Apr 2021 22:27:46 -0400 Subject: [PATCH 22/29] Documentation updates --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 82b0c7ac0..6ca56bbb4 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,9 @@ $ python3 -m pip install crowdstrike-falconpy ``` If you'd like to try the *absolute bleeding edge*, an automated GitHub action releases a test package with every merged pull request containing the string -`[DEPLOY]` in the head of the commit. To install the testing version: +`[DEPLOY]` in the head of the commit. + +To install this testing version of the package, use the command: ```shell $ python3 -m pip install -i https://test.pypi.org/simple crowdstrike-falconpy ``` From 5fdc8502ffb1b2ca4a1f04acaa2d8f6c475b9aa6 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Mon, 12 Apr 2021 01:37:01 -0400 Subject: [PATCH 23/29] Unidiomatic type check fix --- src/falconpy/_util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/falconpy/_util.py b/src/falconpy/_util.py index 008556f6b..94dbdaeb2 100644 --- a/src/falconpy/_util.py +++ b/src/falconpy/_util.py @@ -75,7 +75,7 @@ def validate_payload(validator: dict, params: dict, required: list = None) -> bo def parse_id_list(id_list) -> str: """ Converts a list of IDs to a comma-delimited string """ - if type(id_list) is list: + if isinstance(id_list, list): returned = "" for s in id_list: if len(returned) > 1: From d3c9bbc73c6b3f04b576ea41fec45a5a9d067e57 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Mon, 12 Apr 2021 01:37:23 -0400 Subject: [PATCH 24/29] Safer default payload dictionaries --- src/falconpy/cloud_connect_aws.py | 16 +++++-- src/falconpy/cspm_registration.py | 26 +++++++++--- src/falconpy/custom_ioa.py | 32 ++++++++++---- src/falconpy/detects.py | 4 +- src/falconpy/device_control_policies.py | 16 +++++-- src/falconpy/falconx_sandbox.py | 8 +++- src/falconpy/firewall_management.py | 36 ++++++++++++---- src/falconpy/firewall_policies.py | 20 ++++++--- src/falconpy/host_group.py | 16 +++++-- src/falconpy/hosts.py | 12 ++++-- src/falconpy/incidents.py | 12 ++++-- src/falconpy/intel.py | 32 ++++++++++---- src/falconpy/iocs.py | 4 +- src/falconpy/prevention_policy.py | 16 +++++-- src/falconpy/quick_scan.py | 4 +- src/falconpy/real_time_response.py | 24 ++++++++--- src/falconpy/real_time_response_admin.py | 12 ++++-- src/falconpy/sample_uploads.py | 14 ++++++- src/falconpy/sensor_download.py | 53 +++++++++++++++++++++--- src/falconpy/sensor_update_policy.py | 24 ++++++++--- 20 files changed, 296 insertions(+), 85 deletions(-) diff --git a/src/falconpy/cloud_connect_aws.py b/src/falconpy/cloud_connect_aws.py index 061dcf89e..55f65f8ad 100644 --- a/src/falconpy/cloud_connect_aws.py +++ b/src/falconpy/cloud_connect_aws.py @@ -44,13 +44,15 @@ class Cloud_Connect_AWS(ServiceClass): """ The only requirement to instantiate an instance of this class is a valid token provided by the Falcon API SDK OAuth2 class. """ - def QueryAWSAccounts(self: object, parameters: dict = {}) -> dict: + def QueryAWSAccounts(self: object, parameters: dict = None) -> dict: """ Search for provisioned AWS Accounts by providing an FQL filter and paging details. Returns a set of AWS accounts which match the filter criteria. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/cloud-connect-aws/QueryAWSAccounts FULL_URL = self.base_url+'/cloud-connect-aws/combined/accounts/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters VALIDATOR = { "limit": int, @@ -87,10 +89,12 @@ def GetAWSAccounts(self: object, ids) -> dict: return returned - def ProvisionAWSAccounts(self: object, body: dict, parameters: dict = {}) -> dict: + def ProvisionAWSAccounts(self: object, body: dict, parameters: dict = None) -> dict: """ Provision AWS Accounts by specifying details about the accounts to provision. """ # [POST] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/cloud-connect-aws/ProvisionAWSAccounts FULL_URL = self.base_url+'/cloud-connect-aws/entities/accounts/v1' + if parameters is None: + parameters = {} PARAMS = parameters BODY = body HEADERS = self.headers @@ -156,12 +160,14 @@ def CreateOrUpdateAWSSettings(self: object, body: dict) -> dict: ) return returned - def VerifyAWSAccountAccess(self: object, ids, body: dict = {}) -> dict: + def VerifyAWSAccountAccess(self: object, ids, body: dict = None) -> dict: """ Performs an Access Verification check on the specified AWS Account IDs. """ # [POST] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/cloud-connect-aws/VerifyAWSAccountAccess ID_LIST = str(parse_id_list(ids)).replace(",", "&ids=") FULL_URL = self.base_url+'/cloud-connect-aws/entities/verify-account-access/v1?ids={}'.format(ID_LIST) HEADERS = self.headers + if body is None: + body = {} BODY = body returned = service_request(caller=self, method="POST", @@ -172,13 +178,15 @@ def VerifyAWSAccountAccess(self: object, ids, body: dict = {}) -> dict: ) return returned - def QueryAWSAccountsForIDs(self: object, parameters: dict = {}) -> dict: + def QueryAWSAccountsForIDs(self: object, parameters: dict = None) -> dict: """ Search for provisioned AWS Accounts by providing an FQL filter and paging details. Returns a set of AWS account IDs which match the filter criteria. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/cloud-connect-aws/QueryAWSAccountsForIDs FULL_URL = self.base_url+'/cloud-connect-aws/queries/accounts/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters VALIDATOR = { "limit": int, diff --git a/src/falconpy/cspm_registration.py b/src/falconpy/cspm_registration.py index bb54c40f1..b534016ae 100644 --- a/src/falconpy/cspm_registration.py +++ b/src/falconpy/cspm_registration.py @@ -44,7 +44,7 @@ class CSPM_Registration(ServiceClass): """The only requirement to instantiate an instance of this class is a valid token provided by the Falcon API SDK OAuth2 class. """ - def GetCSPMAwsAccount(self: object, ids, org_ids=None, parameters: dict = {}) -> dict: + def GetCSPMAwsAccount(self: object, ids, org_ids=None, parameters: dict = None) -> dict: """Returns information about the current status of an AWS account. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/cspm-registration/GetCSPMAwsAccount ID_LIST = str(parse_id_list(ids)).replace(",", "&ids=") @@ -53,6 +53,8 @@ def GetCSPMAwsAccount(self: object, ids, org_ids=None, parameters: dict = {}) -> ORG_ID_LIST = str(parse_id_list(org_ids)).replace(",", "&organization-ids=") FULL_URL = FULL_URL + "&organization-id={}".format(ORG_ID_LIST) HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -127,13 +129,15 @@ def GetCSPMAwsAccountScriptsAttachment(self: object) -> bytes: return returned - def GetCSPMAzureAccount(self: object, ids, parameters: dict = {}) -> dict: + def GetCSPMAzureAccount(self: object, ids, parameters: dict = None) -> dict: """Return information about Azure account registration.""" # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/cspm-registration/GetCSPMAzureAccount2 # This shows up as GetCSPMAzureAccount2 in the Uber class. ID_LIST = str(parse_id_list(ids)).replace(",", "&ids=") FULL_URL = self.base_url+"/cloud-connect-cspm-azure/entities/account/v1?ids={}".format(ID_LIST) HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -178,7 +182,7 @@ def DeleteCSPMAzureAccount(self: object, ids) -> dict: return returned # TODO: Confirm payload formats for IDs not passed via an array - def UpdateCSPMAzureAccountClientID(self: object, body: dict = {}, parameters: dict = {}) -> dict: + def UpdateCSPMAzureAccountClientID(self: object, body: dict = None, parameters: dict = None) -> dict: """Update an Azure service account in our system with the user-created client_id created with the public key we've provided. """ @@ -186,8 +190,12 @@ def UpdateCSPMAzureAccountClientID(self: object, body: dict = {}, parameters: di # ... /cspm-registration/UpdateCSPMAzureAccountClientID2 # This shows up as UpdateCSPMAzureAccountClientID2 in the Uber class FULL_URL = self.base_url+'/cloud-connect-cspm-azure/entities/client-id/v1' + if parameters is None: + parameters = {} PARAMS = parameters HEADERS = self.headers + if body is None: + body = {} BODY = body returned = service_request(caller=self, method="PATCH", @@ -199,13 +207,15 @@ def UpdateCSPMAzureAccountClientID(self: object, body: dict = {}, parameters: di ) return returned - def GetCSPMAzureUserScriptsAttachment(self: object, parameters: dict = {}) -> bytes: + def GetCSPMAzureUserScriptsAttachment(self: object, parameters: dict = None) -> bytes: """Return a script for customers to run in their cloud environment to grant access to CrowdStrike for their Azure environment. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html# # ... /cspm-registration/GetCSPMAzureUserScriptsAttachment2 FULL_URL = self.base_url+"/cloud-connect-cspm-azure/entities/user-scripts-download/v1" + if parameters is None: + parameters = {} PARAMS = parameters HEADERS = self.headers returned = service_request(caller=self, @@ -235,11 +245,13 @@ def GetCSPMPolicy(self: object, ids) -> dict: return returned - def GetCSPMPolicySettings(self: object, parameters: dict = {}) -> dict: + def GetCSPMPolicySettings(self: object, parameters: dict = None) -> dict: """Returns information about current policy settings.""" # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/cspm-registration/GetCSPMPolicySettings FULL_URL = self.base_url+"/settings/entities/policy/v1" HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -267,11 +279,13 @@ def UpdateCSPMPolicySettings(self: object, body: dict) -> dict: ) return returned - def GetCSPMScanSchedule(self: object, parameters: dict = {}) -> dict: + def GetCSPMScanSchedule(self: object, parameters: dict = None) -> dict: """Returns scan schedule configuration for one or more cloud platforms.""" # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/cspm-registration/GetCSPMScanSchedule FULL_URL = self.base_url+"/settings/scan-schedule/v1" HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", diff --git a/src/falconpy/custom_ioa.py b/src/falconpy/custom_ioa.py index f901dc9d9..2ab78c333 100644 --- a/src/falconpy/custom_ioa.py +++ b/src/falconpy/custom_ioa.py @@ -102,13 +102,15 @@ def create_rule_groupMixin0(self: object, body: dict, cs_username: str) -> dict: ) return returned - def delete_rule_groupMixin0(self: object, ids, cs_username: str, parameters: dict = {}) -> dict: + def delete_rule_groupMixin0(self: object, ids, cs_username: str, parameters: dict = None) -> dict: """Delete rule groups by ID.""" # [DELETE] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/custom-ioa/delete-rule-groupsMixin0 ID_LIST = str(parse_id_list(ids)).replace(",", "&ids=") FULL_URL = self.base_url+'/ioarules/entities/rule-groups/v1?ids={}'.format(ID_LIST) HEADERS = self.headers HEADERS['X-CS-USERNAME'] = cs_username + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="DELETE", @@ -197,13 +199,15 @@ def create_rule(self: object, body: dict, cs_username: str) -> dict: ) return returned - def delete_rules(self: object, ids, cs_username: str, parameters: dict = {}) -> dict: + def delete_rules(self: object, ids, cs_username: str, parameters: dict = None) -> dict: """Delete rules from a rule group by ID.""" # [DELETE] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/custom-ioa/delete-rules ID_LIST = str(parse_id_list(ids)).replace(",", "&ids=") FULL_URL = self.base_url+'/ioarules/entities/rules/v1?ids={}'.format(ID_LIST) HEADERS = self.headers HEADERS['X-CS-USERNAME'] = cs_username + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="DELETE", @@ -245,11 +249,13 @@ def validate(self: object, body: dict) -> dict: ) return returned - def query_patterns(self: object, parameters: dict = {}) -> dict: + def query_patterns(self: object, parameters: dict = None) -> dict: """Get all pattern severity IDs""" # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/custom-ioa/query-patterns FULL_URL = self.base_url+"/ioarules/queries/pattern-severities/v1" HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -260,11 +266,13 @@ def query_patterns(self: object, parameters: dict = {}) -> dict: ) return returned - def query_platformsMixin0(self: object, parameters: dict = {}) -> dict: + def query_platformsMixin0(self: object, parameters: dict = None) -> dict: """Get all platform IDs.""" # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/custom-ioa/query-platformsMixin0 FULL_URL = self.base_url+"/ioarules/queries/platforms/v1" HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -275,11 +283,13 @@ def query_platformsMixin0(self: object, parameters: dict = {}) -> dict: ) return returned - def query_rule_groups_full(self: object, parameters: dict = {}) -> dict: + def query_rule_groups_full(self: object, parameters: dict = None) -> dict: """Find all rule groups matching the query with optional filter.""" # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/custom-ioa/query-rule-groups-full FULL_URL = self.base_url+"/ioarules/queries/rule-groups-full/v1" HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -290,11 +300,13 @@ def query_rule_groups_full(self: object, parameters: dict = {}) -> dict: ) return returned - def query_rule_groupsMixin0(self: object, parameters: dict = {}) -> dict: + def query_rule_groupsMixin0(self: object, parameters: dict = None) -> dict: """Finds all rule group IDs matching the query with optional filter.""" # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/custom-ioa/query-rule-groupsMixin0 FULL_URL = self.base_url+"/ioarules/queries/rule-groups/v1" HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -305,11 +317,13 @@ def query_rule_groupsMixin0(self: object, parameters: dict = {}) -> dict: ) return returned - def query_rule_types(self: object, parameters: dict = {}) -> dict: + def query_rule_types(self: object, parameters: dict = None) -> dict: """Get all rule type IDs.""" # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/custom-ioa/query-rule-types FULL_URL = self.base_url+"/ioarules/queries/rule-types/v1" HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -320,11 +334,13 @@ def query_rule_types(self: object, parameters: dict = {}) -> dict: ) return returned - def query_rulesMixin0(self: object, parameters: dict = {}) -> dict: + def query_rulesMixin0(self: object, parameters: dict = None) -> dict: """Finds all rule IDs matching the query with optional filter.""" # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/custom-ioa/query-rulesMixin0 FULL_URL = self.base_url+"/ioarules/queries/rules/v1" HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", diff --git a/src/falconpy/detects.py b/src/falconpy/detects.py index 47e5e4a4a..b22cfb40d 100644 --- a/src/falconpy/detects.py +++ b/src/falconpy/detects.py @@ -107,11 +107,13 @@ def GetDetectSummaries(self: object, body: dict) -> dict: ) return returned - def QueryDetects(self: object, parameters: dict = {}) -> dict: + def QueryDetects(self: object, parameters: dict = None) -> dict: """ Search for detection IDs that match a given query. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/detects/QueryDetects FULL_URL = self.base_url+'/detects/queries/detects/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters VALIDATOR = { "limit": int, diff --git a/src/falconpy/device_control_policies.py b/src/falconpy/device_control_policies.py index 2a94025df..b3bc12b22 100644 --- a/src/falconpy/device_control_policies.py +++ b/src/falconpy/device_control_policies.py @@ -44,7 +44,7 @@ class Device_Control_Policies(ServiceClass): """ The only requirement to instantiate an instance of this class is a valid token provided by the Falcon API SDK OAuth2 class. """ - def queryCombinedDeviceControlPolicyMembers(self: object, parameters: dict = {}) -> dict: + def queryCombinedDeviceControlPolicyMembers(self: object, parameters: dict = None) -> dict: """ Search for members of a Device Control Policy in your environment by providing an FQL filter and paging details. Returns a set of host details which match the filter criteria. """ @@ -52,6 +52,8 @@ def queryCombinedDeviceControlPolicyMembers(self: object, parameters: dict = {}) # ... /device-control-policies/queryCombinedDeviceControlPolicyMembers FULL_URL = self.base_url+'/policy/combined/device-control-members/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -62,7 +64,7 @@ def queryCombinedDeviceControlPolicyMembers(self: object, parameters: dict = {}) ) return returned - def queryCombinedDeviceControlPolicies(self: object, parameters: dict = {}) -> dict: + def queryCombinedDeviceControlPolicies(self: object, parameters: dict = None) -> dict: """ Search for Device Control Policies in your environment by providing an FQL filter and paging details. Returns a set of Device Control Policies which match the filter criteria. """ @@ -70,6 +72,8 @@ def queryCombinedDeviceControlPolicies(self: object, parameters: dict = {}) -> d # ... /device-control-policies/queryCombinedDeviceControlPolicies FULL_URL = self.base_url+'/policy/combined/device-control/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -188,7 +192,7 @@ def updateDeviceControlPolicies(self: object, body: dict) -> dict: ) return returned - def queryDeviceControlPolicyMembers(self: object, parameters: dict = {}) -> dict: + def queryDeviceControlPolicyMembers(self: object, parameters: dict = None) -> dict: """ Search for members of a Device Control Policy in your environment by providing an FQL filter and paging details. Returns a set of Agent IDs which match the filter criteria. """ @@ -196,6 +200,8 @@ def queryDeviceControlPolicyMembers(self: object, parameters: dict = {}) -> dict # ... /device-control-policies/queryDeviceControlPolicyMembers FULL_URL = self.base_url+'/policy/queries/device-control-members/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -206,7 +212,7 @@ def queryDeviceControlPolicyMembers(self: object, parameters: dict = {}) -> dict ) return returned - def queryDeviceControlPolicies(self: object, parameters: dict = {}) -> dict: + def queryDeviceControlPolicies(self: object, parameters: dict = None) -> dict: """ Search for Device Control Policies in your environment by providing an FQL filter and paging details. Returns a set of Device Control Policy IDs which match the filter criteria. """ @@ -214,6 +220,8 @@ def queryDeviceControlPolicies(self: object, parameters: dict = {}) -> dict: # ... /device-control-policies/queryDeviceControlPolicyMembers FULL_URL = self.base_url+'/policy/queries/device-control/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", diff --git a/src/falconpy/falconx_sandbox.py b/src/falconpy/falconx_sandbox.py index 14152141e..53f2cece9 100644 --- a/src/falconpy/falconx_sandbox.py +++ b/src/falconpy/falconx_sandbox.py @@ -105,13 +105,15 @@ def Submit(self: object, body: dict) -> dict: ) return returned - def QueryReports(self: object, parameters: dict = {}) -> dict: + def QueryReports(self: object, parameters: dict = None) -> dict: """ Find sandbox reports by providing an FQL filter and paging details. Returns a set of report IDs that match your criteria. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/falconx-sandbox/QueryReports FULL_URL = self.base_url+'/falconx/queries/reports/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -122,13 +124,15 @@ def QueryReports(self: object, parameters: dict = {}) -> dict: ) return returned - def QuerySubmissions(self: object, parameters: dict = {}) -> dict: + def QuerySubmissions(self: object, parameters: dict = None) -> dict: """ Find submission IDs for uploaded files by providing an FQL filter and paging details. Returns a set of submission IDs that match your criteria. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/falconx-sandbox/QuerySubmissions FULL_URL = self.base_url+'/falconx/queries/submissions/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", diff --git a/src/falconpy/firewall_management.py b/src/falconpy/firewall_management.py index a3f80871b..eca5ce47e 100644 --- a/src/falconpy/firewall_management.py +++ b/src/falconpy/firewall_management.py @@ -193,12 +193,14 @@ def get_rule_groups(self: object, ids) -> dict: ) return returned - def create_rule_group(self: object, body: dict, cs_username: str, parameters: dict = {}) -> dict: + def create_rule_group(self: object, body: dict, cs_username: str, parameters: dict = None) -> dict: """ Create new rule group on a platform for a customer with a name and description, and return the ID. """ # [POST] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/firewall-management/create_rule_group FULL_URL = self.base_url+'/fwmgr/entities/rule-groups/v1' HEADERS = self.headers HEADERS['X-CS-USERNAME'] = cs_username + if parameters is None: + parameters = {} PARAMS = parameters BODY = body returned = service_request(caller=self, @@ -211,13 +213,15 @@ def create_rule_group(self: object, body: dict, cs_username: str, parameters: di ) return returned - def delete_rule_groups(self: object, ids, cs_username: str, parameters: dict = {}) -> dict: + def delete_rule_groups(self: object, ids, cs_username: str, parameters: dict = None) -> dict: """ Delete rule group entities by ID. """ # [DELETE] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/falconx-sandbox/QueryReports ID_LIST = str(parse_id_list(ids)).replace(",", "&ids=") FULL_URL = self.base_url+'/fwmgr/entities/rule-groups/v1?ids={}'.format(ID_LIST) HEADERS = self.headers HEADERS['X-CS-USERNAME'] = cs_username + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="DELETE", @@ -228,12 +232,14 @@ def delete_rule_groups(self: object, ids, cs_username: str, parameters: dict = { ) return returned - def update_rule_group(self: object, body: dict, cs_username: str, parameters: dict = {}) -> dict: + def update_rule_group(self: object, body: dict, cs_username: str, parameters: dict = None) -> dict: """ Update name, description, or enabled status of a rule group, or create, edit, delete, or reorder rules. """ # [PATCH] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/firewall-management/update_rule_group FULL_URL = self.base_url+'/fwmgr/entities/rule-groups/v1' HEADERS = self.headers HEADERS['X-CS-USERNAME'] = cs_username + if parameters is None: + parameters = {} PARAMS = parameters BODY = body returned = service_request(caller=self, @@ -260,11 +266,13 @@ def get_rules(self: object, ids) -> dict: ) return returned - def query_events(self: object, parameters: dict = {}) -> dict: + def query_events(self: object, parameters: dict = None) -> dict: """ Find all event IDs matching the query with filter. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/firewall-management/query_events FULL_URL = self.base_url+'/fwmgr/queries/events/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -275,11 +283,13 @@ def query_events(self: object, parameters: dict = {}) -> dict: ) return returned - def query_firewall_fields(self: object, parameters: dict = {}) -> dict: + def query_firewall_fields(self: object, parameters: dict = None) -> dict: """ Get the firewall field specification IDs for the provided platform. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/firewall-management/query_firewall_fields FULL_URL = self.base_url+'/fwmgr/queries/firewall-fields/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -290,11 +300,13 @@ def query_firewall_fields(self: object, parameters: dict = {}) -> dict: ) return returned - def query_platforms(self: object, parameters: dict = {}) -> dict: + def query_platforms(self: object, parameters: dict = None) -> dict: """ Get the list of platform names. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/firewall-management/query_platforms FULL_URL = self.base_url+'/fwmgr/queries/platforms/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -305,11 +317,13 @@ def query_platforms(self: object, parameters: dict = {}) -> dict: ) return returned - def query_policy_rules(self: object, parameters: dict = {}) -> dict: + def query_policy_rules(self: object, parameters: dict = None) -> dict: """ Find all firewall rule IDs matching the query with filter, and return them in precedence order. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/firewall-management/query_policy_rules FULL_URL = self.base_url+'/fwmgr/queries/policy-rules/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -320,11 +334,13 @@ def query_policy_rules(self: object, parameters: dict = {}) -> dict: ) return returned - def query_rule_groups(self: object, parameters: dict = {}) -> dict: + def query_rule_groups(self: object, parameters: dict = None) -> dict: """ Find all rule group IDs matching the query with filter. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/firewall-management/query_rule_groups FULL_URL = self.base_url+'/fwmgr/queries/rule-groups/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -335,11 +351,13 @@ def query_rule_groups(self: object, parameters: dict = {}) -> dict: ) return returned - def query_rules(self: object, parameters: dict = {}) -> dict: + def query_rules(self: object, parameters: dict = None) -> dict: """ Find all rule IDs matching the query with filter. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/firewall-management/query_rule_groups FULL_URL = self.base_url+'/fwmgr/queries/rules/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", diff --git a/src/falconpy/firewall_policies.py b/src/falconpy/firewall_policies.py index 0b411e6d2..9d74ce60d 100644 --- a/src/falconpy/firewall_policies.py +++ b/src/falconpy/firewall_policies.py @@ -44,7 +44,7 @@ class Firewall_Policies(ServiceClass): """ The only requirement to instantiate an instance of this class is a valid token provided by the Falcon API SDK OAuth2 class. """ - def queryCombinedFirewallPolicyMembers(self: object, parameters: dict = {}) -> dict: + def queryCombinedFirewallPolicyMembers(self: object, parameters: dict = None) -> dict: """ Search for members of a Firewall Policy in your environment by providing an FQL filter and paging details. Returns a set of host details which match the filter criteria. """ @@ -52,6 +52,8 @@ def queryCombinedFirewallPolicyMembers(self: object, parameters: dict = {}) -> d # ... /firewall-policies/queryCombinedFirewallPolicyMembers FULL_URL = self.base_url+'/policy/combined/firewall-members/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -62,13 +64,15 @@ def queryCombinedFirewallPolicyMembers(self: object, parameters: dict = {}) -> d ) return returned - def queryCombinedFirewallPolicies(self: object, parameters: dict = {}) -> dict: + def queryCombinedFirewallPolicies(self: object, parameters: dict = None) -> dict: """ Search for Firewall Policies in your environment by providing an FQL filter and paging details. Returns a set of Firewall Policies which match the filter criteria. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/firewall-policies/queryCombinedFirewallPolicies FULL_URL = self.base_url+'/policy/combined/firewall/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -137,11 +141,13 @@ def getFirewallPolicies(self: object, ids) -> dict: ) return returned - def createFirewallPolicies(self: object, body: dict, parameters: dict = {}) -> dict: + def createFirewallPolicies(self: object, body: dict, parameters: dict = None) -> dict: """ Create Firewall Policies by specifying details about the policy to create. """ # [POST] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/firewall-policies/createFirewallPolicies FULL_URL = self.base_url+'/policy/entities/firewall/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters BODY = body returned = service_request(caller=self, @@ -183,13 +189,15 @@ def updateFirewallPolicies(self: object, body: dict) -> dict: ) return returned - def queryFirewallPolicyMembers(self: object, parameters: dict = {}) -> dict: + def queryFirewallPolicyMembers(self: object, parameters: dict = None) -> dict: """ Search for members of a Firewall Policy in your environment by providing an FQL filter and paging details. Returns a set of Agent IDs which match the filter criteria. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/firewall-policies/queryFirewallPolicyMembers FULL_URL = self.base_url+'/policy/queries/firewall-members/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -200,13 +208,15 @@ def queryFirewallPolicyMembers(self: object, parameters: dict = {}) -> dict: ) return returned - def queryFirewallPolicies(self: object, parameters: dict = {}) -> dict: + def queryFirewallPolicies(self: object, parameters: dict = None) -> dict: """ Search for Firewall Policies in your environment by providing an FQL filter and paging details. Returns a set of Firewall Policy IDs which match the filter criteria. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/firewall-policies/queryFirewallPolicies FULL_URL = self.base_url+'/policy/queries/firewall/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", diff --git a/src/falconpy/host_group.py b/src/falconpy/host_group.py index f88bb22ee..7575de057 100644 --- a/src/falconpy/host_group.py +++ b/src/falconpy/host_group.py @@ -44,13 +44,15 @@ class Host_Group(ServiceClass): """ The only requirement to instantiate an instance of this class is a valid token provided by the Falcon API SDK OAuth2 class. """ - def queryCombinedGroupMembers(self: object, parameters: dict = {}) -> dict: + def queryCombinedGroupMembers(self: object, parameters: dict = None) -> dict: """ Search for members of a Host Group in your environment by providing an FQL filter and paging details. Returns a set of host details which match the filter criteria. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/host-group/queryCombinedGroupMembers FULL_URL = self.base_url+'/devices/combined/host-group-members/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -61,13 +63,15 @@ def queryCombinedGroupMembers(self: object, parameters: dict = {}) -> dict: ) return returned - def queryCombinedHostGroups(self: object, parameters: dict = {}) -> dict: + def queryCombinedHostGroups(self: object, parameters: dict = None) -> dict: """ Search for Host Groups in your environment by providing an FQL filter and paging details. Returns a set of Host Groups which match the filter criteria. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/host-group/queryCombinedHostGroups FULL_URL = self.base_url+'/devices/combined/host-groups/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -160,13 +164,15 @@ def updateHostGroups(self: object, body: dict) -> dict: ) return returned - def queryGroupMembers(self: object, parameters: dict = {}) -> dict: + def queryGroupMembers(self: object, parameters: dict = None) -> dict: """ Search for members of a Host Group in your environment by providing an FQL filter and paging details. Returns a set of Agent IDs which match the filter criteria. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/host-group/queryGroupMembers FULL_URL = self.base_url+'/devices/queries/host-group-members/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -177,13 +183,15 @@ def queryGroupMembers(self: object, parameters: dict = {}) -> dict: ) return returned - def queryHostGroups(self: object, parameters: dict = {}) -> dict: + def queryHostGroups(self: object, parameters: dict = None) -> dict: """ Search for Host Groups in your environment by providing an FQL filter and paging details. Returns a set of Host Group IDs which match the filter criteria. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/host-group/queryHostGroups FULL_URL = self.base_url+'/devices/queries/host-groups/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", diff --git a/src/falconpy/hosts.py b/src/falconpy/hosts.py index 6878bd433..1ff369cf0 100644 --- a/src/falconpy/hosts.py +++ b/src/falconpy/hosts.py @@ -125,11 +125,13 @@ def GetDeviceDetails(self: object, ids) -> dict: ) return returned - def QueryHiddenDevices(self: object, parameters: dict = {}) -> dict: + def QueryHiddenDevices(self: object, parameters: dict = None) -> dict: """ Perform the specified action on the Prevention Policies specified in the request. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/hosts/QueryHiddenDevices FULL_URL = self.base_url+'/devices/queries/devices-hidden/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -140,11 +142,13 @@ def QueryHiddenDevices(self: object, parameters: dict = {}) -> dict: ) return returned - def QueryDevicesByFilterScroll(self: object, parameters: dict = {}) -> dict: + def QueryDevicesByFilterScroll(self: object, parameters: dict = None) -> dict: """ Perform the specified action on the Prevention Policies specified in the request. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/hosts/QueryDevicesByFilterScroll FULL_URL = self.base_url+'/devices/queries/devices-scroll/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -155,11 +159,13 @@ def QueryDevicesByFilterScroll(self: object, parameters: dict = {}) -> dict: ) return returned - def QueryDevicesByFilter(self: object, parameters: dict = {}) -> dict: + def QueryDevicesByFilter(self: object, parameters: dict = None) -> dict: """ Search for hosts in your environment by platform, hostname, IP, and other criteria. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/hosts/QueryDevicesByFilter FULL_URL = self.base_url+'/devices/queries/devices/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", diff --git a/src/falconpy/incidents.py b/src/falconpy/incidents.py index 49ff0a3f0..a7be5a914 100644 --- a/src/falconpy/incidents.py +++ b/src/falconpy/incidents.py @@ -44,11 +44,13 @@ class Incidents(ServiceClass): """ The only requirement to instantiate an instance of this class is a valid token provided by the Falcon API SDK OAuth2 class. """ - def CrowdScore(self: object, parameters: dict = {}) -> dict: + def CrowdScore(self: object, parameters: dict = None) -> dict: """ Query environment wide CrowdScore and return the entity data. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/incidents/CrowdScore FULL_URL = self.base_url+'/incidents/combined/crowdscores/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -106,11 +108,13 @@ def GetIncidents(self: object, body: dict) -> dict: ) return returned - def QueryBehaviors(self: object, parameters: dict = {}) -> dict: + def QueryBehaviors(self: object, parameters: dict = None) -> dict: """ Search for behaviors by providing an FQL filter, sorting, and paging details. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/incidents/QueryBehaviors FULL_URL = self.base_url+'/incidents/queries/behaviors/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -121,11 +125,13 @@ def QueryBehaviors(self: object, parameters: dict = {}) -> dict: ) return returned - def QueryIncidents(self: object, parameters: dict = {}) -> dict: + def QueryIncidents(self: object, parameters: dict = None) -> dict: """ Search for incidents by providing an FQL filter, sorting, and paging details. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/incidents/QueryIncidents FULL_URL = self.base_url+'/incidents/queries/incidents/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", diff --git a/src/falconpy/intel.py b/src/falconpy/intel.py index 02be67ae2..5be22e409 100644 --- a/src/falconpy/intel.py +++ b/src/falconpy/intel.py @@ -44,11 +44,13 @@ class Intel(ServiceClass): """ The only requirement to instantiate an instance of this class is a valid token provided by the Falcon API SDK OAuth2 class. """ - def QueryIntelActorEntities(self: object, parameters: dict = {}) -> dict: + def QueryIntelActorEntities(self: object, parameters: dict = None) -> dict: """ Get info about actors that match provided FQL filters. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/intel/QueryIntelActorEntities FULL_URL = self.base_url+'/intel/combined/actors/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -59,11 +61,13 @@ def QueryIntelActorEntities(self: object, parameters: dict = {}) -> dict: ) return returned - def QueryIntelIndicatorEntities(self: object, parameters: dict = {}) -> dict: + def QueryIntelIndicatorEntities(self: object, parameters: dict = None) -> dict: """ Get info about indicators that match provided FQL filters. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/intel/QueryIntelIndicatorEntities FULL_URL = self.base_url+'/intel/combined/indicators/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -74,11 +78,13 @@ def QueryIntelIndicatorEntities(self: object, parameters: dict = {}) -> dict: ) return returned - def QueryIntelReportEntities(self: object, parameters: dict = {}) -> dict: + def QueryIntelReportEntities(self: object, parameters: dict = None) -> dict: """ Get info about reports that match provided FQL filters. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/intel/QueryIntelReportEntities FULL_URL = self.base_url+'/intel/combined/reports/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -89,12 +95,14 @@ def QueryIntelReportEntities(self: object, parameters: dict = {}) -> dict: ) return returned - def GetIntelActorEntities(self: object, ids, parameters: dict = {}) -> dict: + def GetIntelActorEntities(self: object, ids, parameters: dict = None) -> dict: """ Retrieve specific actors using their actor IDs. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/intel/GetIntelActorEntities ID_LIST = str(parse_id_list(ids)).replace(",", "&ids=") FULL_URL = self.base_url+'/intel/entities/actors/v1?ids={}'.format(ID_LIST) HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -135,12 +143,14 @@ def GetIntelReportPDF(self: object, parameters: dict) -> dict: ) return returned - def GetIntelReportEntities(self: object, ids, parameters: dict = {}) -> dict: + def GetIntelReportEntities(self: object, ids, parameters: dict = None) -> dict: """ Retrieve specific reports using their report IDs. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/intel/GetIntelReportEntities ID_LIST = str(parse_id_list(ids)).replace(",", "&ids=") FULL_URL = self.base_url+'/intel/entities/reports/v1?ids={}'.format(ID_LIST) HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -197,11 +207,13 @@ def GetIntelRuleEntities(self: object, ids) -> dict: ) return returned - def QueryIntelActorIds(self: object, parameters: dict = {}) -> dict: + def QueryIntelActorIds(self: object, parameters: dict = None) -> dict: """ Get actor IDs that match provided FQL filters. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/intel/QueryIntelActorIds FULL_URL = self.base_url+'/intel/queries/actors/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -212,11 +224,13 @@ def QueryIntelActorIds(self: object, parameters: dict = {}) -> dict: ) return returned - def QueryIntelIndicatorIds(self: object, parameters: dict = {}) -> dict: + def QueryIntelIndicatorIds(self: object, parameters: dict = None) -> dict: """ Get indicators IDs that match provided FQL filters. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/intel/QueryIntelIndicatorIds FULL_URL = self.base_url+'/intel/queries/indicators/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -227,11 +241,13 @@ def QueryIntelIndicatorIds(self: object, parameters: dict = {}) -> dict: ) return returned - def QueryIntelReportIds(self: object, parameters: dict = {}) -> dict: + def QueryIntelReportIds(self: object, parameters: dict = None) -> dict: """ Get report IDs that match provided FQL filters. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/intel/QueryIntelReportIds FULL_URL = self.base_url+'/intel/queries/reports/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", diff --git a/src/falconpy/iocs.py b/src/falconpy/iocs.py index 234a3ffe0..9ab40139b 100644 --- a/src/falconpy/iocs.py +++ b/src/falconpy/iocs.py @@ -138,11 +138,13 @@ def DevicesRanOn(self: object, parameters: dict) -> dict: ) return returned - def QueryIOCs(self: object, parameters: dict = {}) -> dict: + def QueryIOCs(self: object, parameters: dict = None) -> dict: """ Search the custom IOCs in your customer account. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/iocs/QueryIOCs FULL_URL = self.base_url+'/indicators/queries/iocs/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", diff --git a/src/falconpy/prevention_policy.py b/src/falconpy/prevention_policy.py index 04d4f4a05..38cc11072 100644 --- a/src/falconpy/prevention_policy.py +++ b/src/falconpy/prevention_policy.py @@ -44,7 +44,7 @@ class Prevention_Policy(ServiceClass): """ The only requirement to instantiate an instance of this class is a valid token provided by the Falcon API SDK OAuth2 class. """ - def queryCombinedPreventionPolicyMembers(self: object, parameters: dict = {}) -> dict: + def queryCombinedPreventionPolicyMembers(self: object, parameters: dict = None) -> dict: """ Search for members of a Prevention Policy in your environment by providing an FQL filter and paging details. Returns a set of host details which match the filter criteria. """ @@ -52,6 +52,8 @@ def queryCombinedPreventionPolicyMembers(self: object, parameters: dict = {}) -> # ... /prevention-policies/queryCombinedPreventionPolicyMembers FULL_URL = self.base_url+'/policy/combined/prevention-members/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -62,7 +64,7 @@ def queryCombinedPreventionPolicyMembers(self: object, parameters: dict = {}) -> ) return returned - def queryCombinedPreventionPolicies(self: object, parameters: dict = {}) -> dict: + def queryCombinedPreventionPolicies(self: object, parameters: dict = None) -> dict: """ Search for Prevention Policies in your environment by providing an FQL filter and paging details. Returns a set of Prevention Policies which match the filter criteria. """ @@ -70,6 +72,8 @@ def queryCombinedPreventionPolicies(self: object, parameters: dict = {}) -> dict # ... /prevention-policies/queryCombinedPreventionPolicies FULL_URL = self.base_url+'/policy/combined/prevention/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -182,7 +186,7 @@ def updatePreventionPolicies(self: object, body: dict) -> dict: ) return returned - def queryPreventionPolicyMembers(self: object, parameters: dict = {}) -> dict: + def queryPreventionPolicyMembers(self: object, parameters: dict = None) -> dict: """ Search for members of a Prevention Policy in your environment by providing an FQL filter and paging details. Returns a set of Agent IDs which match the filter criteria. """ @@ -190,6 +194,8 @@ def queryPreventionPolicyMembers(self: object, parameters: dict = {}) -> dict: # ... /prevention-policies/queryPreventionPolicyMembers FULL_URL = self.base_url+'/policy/queries/prevention-members/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -200,13 +206,15 @@ def queryPreventionPolicyMembers(self: object, parameters: dict = {}) -> dict: ) return returned - def queryPreventionPolicies(self: object, parameters: dict = {}) -> dict: + def queryPreventionPolicies(self: object, parameters: dict = None) -> dict: """ Search for Prevention Policies in your environment by providing an FQL filter and paging details. Returns a set of Prevention Policy IDs which match the filter criteria. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/prevention-policies/queryPreventionPolicies FULL_URL = self.base_url+'/policy/queries/prevention/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", diff --git a/src/falconpy/quick_scan.py b/src/falconpy/quick_scan.py index 982859f3f..5cd056dfe 100644 --- a/src/falconpy/quick_scan.py +++ b/src/falconpy/quick_scan.py @@ -90,13 +90,15 @@ def ScanSamples(self: object, body: dict) -> dict: ) return returned - def QuerySubmissionsMixin0(self: object, parameters: dict = {}) -> dict: + def QuerySubmissionsMixin0(self: object, parameters: dict = None) -> dict: """Find IDs for submitted scans by providing an FQL filter and paging details. Returns a set of volume IDs that match your criteria. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/quick-scan/QuerySubmissionsMixin0 FULL_URL = self.base_url+"/scanner/queries/scans/v1" HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", diff --git a/src/falconpy/real_time_response.py b/src/falconpy/real_time_response.py index 222bc15dd..79e7b7c96 100644 --- a/src/falconpy/real_time_response.py +++ b/src/falconpy/real_time_response.py @@ -59,12 +59,14 @@ def RTR_AggregateSessions(self: object, body: dict) -> dict: ) return returned - def BatchActiveResponderCmd(self: object, body: dict, parameters: dict = {}) -> dict: + def BatchActiveResponderCmd(self: object, body: dict, parameters: dict = None) -> dict: """ Batch executes a RTR active-responder command across the hosts mapped to the given batch ID. """ # [POST] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/real-time-response/BatchActiveResponderCmd FULL_URL = self.base_url+'/real-time-response/combined/batch-active-responder-command/v1' HEADERS = self.headers BODY = body + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="POST", @@ -76,12 +78,14 @@ def BatchActiveResponderCmd(self: object, body: dict, parameters: dict = {}) -> ) return returned - def BatchCmd(self: object, body: dict, parameters: dict = {}) -> dict: + def BatchCmd(self: object, body: dict, parameters: dict = None) -> dict: """ Batch executes a RTR read-only command across the hosts mapped to the given batch ID. """ # [POST] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/real-time-response/BatchActiveResponderCmd FULL_URL = self.base_url+'/real-time-response/combined/batch-command/v1' HEADERS = self.headers BODY = body + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="POST", @@ -110,7 +114,7 @@ def BatchGetCmdStatus(self: object, parameters: dict) -> dict: ) return returned - def BatchGetCmd(self: object, body: dict, parameters: dict = {}) -> dict: + def BatchGetCmd(self: object, body: dict, parameters: dict = None) -> dict: """ Batch executes `get` command across hosts to retrieve files. After this call is made `/real-time-response/combined/get-command-status/v1` is used to query for the results. """ @@ -118,6 +122,8 @@ def BatchGetCmd(self: object, body: dict, parameters: dict = {}) -> dict: FULL_URL = self.base_url+'/real-time-response/combined/batch-get-command/v1' HEADERS = self.headers BODY = body + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="POST", @@ -129,7 +135,7 @@ def BatchGetCmd(self: object, body: dict, parameters: dict = {}) -> dict: ) return returned - def BatchInitSessions(self: object, body: dict, parameters: dict = {}) -> dict: + def BatchInitSessions(self: object, body: dict, parameters: dict = None) -> dict: """ Batch initialize a RTR session on multiple hosts. Before any RTR commands can be used, an active session is needed on the host. """ @@ -137,6 +143,8 @@ def BatchInitSessions(self: object, body: dict, parameters: dict = {}) -> dict: FULL_URL = self.base_url+'/real-time-response/combined/batch-init-session/v1' HEADERS = self.headers BODY = body + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="POST", @@ -148,12 +156,14 @@ def BatchInitSessions(self: object, body: dict, parameters: dict = {}) -> dict: ) return returned - def BatchRefreshSessions(self: object, body: dict, parameters: dict = {}) -> dict: + def BatchRefreshSessions(self: object, body: dict, parameters: dict = None) -> dict: """ Batch refresh a RTR session on multiple hosts. RTR sessions will expire after 10 minutes unless refreshed. """ # [POST] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/real-time-response/BatchRefreshSessions FULL_URL = self.base_url+'/real-time-response/combined/batch-refresh-session/v1' HEADERS = self.headers BODY = body + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="POST", @@ -362,11 +372,13 @@ def RTR_DeleteQueuedSession(self: object, parameters: dict) -> dict: ) return returned - def RTR_ListAllSessions(self: object, parameters: dict = {}) -> dict: + def RTR_ListAllSessions(self: object, parameters: dict = None) -> dict: """ Get a list of session_ids. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/real-time-response/RTR_ListAllSessions FULL_URL = self.base_url+'/real-time-response/queries/sessions/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", diff --git a/src/falconpy/real_time_response_admin.py b/src/falconpy/real_time_response_admin.py index ac9060fe8..bc79e600b 100644 --- a/src/falconpy/real_time_response_admin.py +++ b/src/falconpy/real_time_response_admin.py @@ -44,12 +44,14 @@ class Real_Time_Response_Admin(ServiceClass): """ The only requirement to instantiate an instance of this class is a valid token provided by the Falcon API SDK OAuth2 class. """ - def BatchAdminCmd(self: object, body: dict, parameters: dict = {}) -> dict: + def BatchAdminCmd(self: object, body: dict, parameters: dict = None) -> dict: """ Batch executes a RTR administrator command across the hosts mapped to the given batch ID. """ # [POST] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/real-time-response-admin/BatchAdminCmd FULL_URL = self.base_url+'/real-time-response/combined/batch-admin-command/v1' HEADERS = self.headers BODY = body + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="POST", @@ -203,11 +205,13 @@ def RTR_UpdateScripts(self: object, data, files) -> dict: ) return returned - def RTR_ListPut_Files(self: object, parameters: dict = {}) -> dict: + def RTR_ListPut_Files(self: object, parameters: dict = None) -> dict: """ Get a list of put-file ID's that are available to the user for the `put` command. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/real-time-response-admin/RTR_ListPut_Files FULL_URL = self.base_url+'/real-time-response/queries/put-files/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -218,11 +222,13 @@ def RTR_ListPut_Files(self: object, parameters: dict = {}) -> dict: ) return returned - def RTR_ListScripts(self: object, parameters: dict = {}) -> dict: + def RTR_ListScripts(self: object, parameters: dict = None) -> dict: """ Get a list of custom-script ID's that are available to the user for the `runscript` command. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/real-time-response-admin/RTR_ListScripts FULL_URL = self.base_url+'/real-time-response/queries/scripts/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", diff --git a/src/falconpy/sample_uploads.py b/src/falconpy/sample_uploads.py index be0d9fba3..fa2880fba 100644 --- a/src/falconpy/sample_uploads.py +++ b/src/falconpy/sample_uploads.py @@ -44,12 +44,14 @@ class Sample_Uploads(ServiceClass): """ The only requirement to instantiate an instance of this class is a valid token provided by the Falcon API SDK OAuth2 class. """ - def GetSampleV3(self: object, ids: list or str, parameters: dict = {}) -> object: + def GetSampleV3(self: object, ids: list or str, parameters: dict = None) -> object: """ Retrieves the file associated with the given ID (SHA256)""" # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/sample-uploads/GetSampleV3 ID_LIST = str(parse_id_list(ids)).replace(",", "&ids=") FULL_URL = self.base_url+'/samples/entities/samples/v3?ids={}'.format(ID_LIST) HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -60,14 +62,22 @@ def GetSampleV3(self: object, ids: list or str, parameters: dict = {}) -> object ) return returned - def UploadSampleV3(self: object, file_data: object, body: dict = {}, file_name: str = "", parameters: dict = {}) -> dict: + def UploadSampleV3(self: object, + file_data: object, + body: dict = None, + file_name: str = "", + parameters: dict = None) -> dict: """ Upload a file for further cloud analysis. After uploading, call the specific analysis API endpoint. """ # [POST] https://assets.falcon.crowdstrike.com/support/api/swagger.html#/sample-uploads/UploadSampleV3 FULL_URL = self.base_url+f"/samples/entities/samples/v3?file_name={str(file_name)}" HEADERS = self.headers HEADERS["Content-Type"] = "application/octet-stream" + if parameters is None: + parameters = {} PARAMS = parameters DATA = file_data + if body is None: + body = {} BODY = body returned = service_request(caller=self, method="POST", diff --git a/src/falconpy/sensor_download.py b/src/falconpy/sensor_download.py index d14bce7fc..1f9d05d4e 100644 --- a/src/falconpy/sensor_download.py +++ b/src/falconpy/sensor_download.py @@ -1,17 +1,58 @@ -from ._util import service_request, parse_id_list, generate_ok_result -from ._service_class import ServiceClass +""" + _______ __ _______ __ __ __ +| _ .----.-----.--.--.--.--| | _ | |_.----|__| |--.-----. +|. 1___| _| _ | | | | _ | 1___| _| _| | <| -__| +|. |___|__| |_____|________|_____|____ |____|__| |__|__|__|_____| +|: 1 | |: 1 | +|::.. . | CROWDSTRIKE FALCON |::.. . | FalconPy +`-------' `-------' + +OAuth2 API - Customer SDK + +sensor_download - Falcon Sensor Download API Interface Class + +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 +""" import os +from ._util import service_request, parse_id_list, generate_ok_result +from ._service_class import ServiceClass class Sensor_Download(ServiceClass): - - def GetCombinedSensorInstallersByQuery(self: object, parameters: dict = {}) -> dict: + """The only requirement to instantiate an instance of this class + is a valid token provided by the Falcon API SDK OAuth2 class. + """ + def GetCombinedSensorInstallersByQuery(self: object, parameters: dict = None) -> dict: """ Retrieve all metadata for installers from provided query """ FULL_URL = self.base_url+'/sensors/combined/installers/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -79,12 +120,14 @@ def GetSensorInstallersCCIDByQuery(self: object) -> dict: ) return returned - def GetSensorInstallersByQuery(self: object, parameters: dict = {}) -> dict: + def GetSensorInstallersByQuery(self: object, parameters: dict = None) -> dict: """ Retrieve a list of SHA256 for installers based on the filter """ FULL_URL = self.base_url+'/sensors/queries/installers/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", diff --git a/src/falconpy/sensor_update_policy.py b/src/falconpy/sensor_update_policy.py index 4ef03ef6f..26fa9f08e 100644 --- a/src/falconpy/sensor_update_policy.py +++ b/src/falconpy/sensor_update_policy.py @@ -61,12 +61,14 @@ def revealUninstallToken(self: object, body: dict) -> dict: ) return returned - def queryCombinedSensorUpdateBuilds(self: object, parameters: dict = {}) -> dict: + def queryCombinedSensorUpdateBuilds(self: object, parameters: dict = None) -> dict: """ Retrieve available builds for use with Sensor Update Policies. """ # [GET] https://assets.falcon.crowdstrike.com/support/api/swagger.html# # ... /sensor-update-policies/queryCombinedSensorUpdateBuilds FULL_URL = self.base_url+'/policy/combined/sensor-update-builds/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -77,7 +79,7 @@ def queryCombinedSensorUpdateBuilds(self: object, parameters: dict = {}) -> dict ) return returned - def queryCombinedSensorUpdatePolicyMembers(self: object, parameters: dict = {}) -> dict: + def queryCombinedSensorUpdatePolicyMembers(self: object, parameters: dict = None) -> dict: """ Search for members of a Sensor Update Policy in your environment by providing an FQL filter and paging details. Returns a set of host details which match the filter criteria. """ @@ -85,6 +87,8 @@ def queryCombinedSensorUpdatePolicyMembers(self: object, parameters: dict = {}) # ... /sensor-update-policies/queryCombinedSensorUpdatePolicyMembers FULL_URL = self.base_url+'/policy/combined/sensor-update-members/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -95,7 +99,7 @@ def queryCombinedSensorUpdatePolicyMembers(self: object, parameters: dict = {}) ) return returned - def queryCombinedSensorUpdatePolicies(self: object, parameters: dict = {}) -> dict: + def queryCombinedSensorUpdatePolicies(self: object, parameters: dict = None) -> dict: """ Search for Sensor Update Policies in your environment by providing an FQL filter and paging details. Returns a set of Sensor Update Policies which match the filter criteria. """ @@ -103,6 +107,8 @@ def queryCombinedSensorUpdatePolicies(self: object, parameters: dict = {}) -> di # ... /sensor-update-policies/queryCombinedSensorUpdatePolicies FULL_URL = self.base_url+'/policy/combined/sensor-update/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -113,7 +119,7 @@ def queryCombinedSensorUpdatePolicies(self: object, parameters: dict = {}) -> di ) return returned - def queryCombinedSensorUpdatePoliciesV2(self: object, parameters: dict = {}) -> dict: + def queryCombinedSensorUpdatePoliciesV2(self: object, parameters: dict = None) -> dict: """ Search for Sensor Update Policies with additional support for uninstall protection in your environment by providing an FQL filter and paging details. Returns a set of Sensor Update Policies which match the filter criteria. @@ -122,6 +128,8 @@ def queryCombinedSensorUpdatePoliciesV2(self: object, parameters: dict = {}) -> # ... /sensor-update-policies/queryCombinedSensorUpdatePoliciesV2 FULL_URL = self.base_url+'/policy/combined/sensor-update/v2' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -290,7 +298,7 @@ def updateSensorUpdatePoliciesV2(self: object, body: dict) -> dict: ) return returned - def querySensorUpdatePolicyMembers(self: object, parameters: dict = {}) -> dict: + def querySensorUpdatePolicyMembers(self: object, parameters: dict = None) -> dict: """ Search for members of a Sensor Update Policy in your environment by providing an FQL filter and paging details. Returns a set of Agent IDs which match the filter criteria. """ @@ -298,6 +306,8 @@ def querySensorUpdatePolicyMembers(self: object, parameters: dict = {}) -> dict: # ... /sensor-update-policies/querySensorUpdatePolicyMembers FULL_URL = self.base_url+'/policy/queries/sensor-update-members/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", @@ -308,7 +318,7 @@ def querySensorUpdatePolicyMembers(self: object, parameters: dict = {}) -> dict: ) return returned - def querySensorUpdatePolicies(self: object, parameters: dict = {}) -> dict: + def querySensorUpdatePolicies(self: object, parameters: dict = None) -> dict: """ Search for Sensor Update Policies in your environment by providing an FQL filter and paging details. Returns a set of Sensor Update Policy IDs which match the filter criteria. """ @@ -316,6 +326,8 @@ def querySensorUpdatePolicies(self: object, parameters: dict = {}) -> dict: # ... /sensor-update-policies/querySensorUpdatePolicies FULL_URL = self.base_url+'/policy/queries/sensor-update/v1' HEADERS = self.headers + if parameters is None: + parameters = {} PARAMS = parameters returned = service_request(caller=self, method="GET", From 929a283d7faa05123d6d4926361b65d76abb69ca Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Mon, 12 Apr 2021 01:58:59 -0400 Subject: [PATCH 25/29] Safer payload default dictionaries and lists --- src/falconpy/api_complete.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/falconpy/api_complete.py b/src/falconpy/api_complete.py index 049053e27..5a9ba66b4 100644 --- a/src/falconpy/api_complete.py +++ b/src/falconpy/api_complete.py @@ -107,9 +107,9 @@ def deauthenticate(self: object) -> bool: # NOTE: Not specifying datatypes for "ids" and "partition" parameters # to allow developers to pass str / lists / integers as necessary - def command(self: object, action: str = "", parameters: dict = {}, body: dict = {}, data: dict = {}, - headers: dict = {}, ids=None, partition=None, override: str = None, action_name: str = None, - files: list = [], file_name: str = None, content_type: str = None): # May return dict or object datatypes + def command(self: object, action: str = "", parameters: dict = None, body: dict = None, data: dict = None, + headers: dict = None, ids=None, partition=None, override: str = None, action_name: str = None, + files: list = None, file_name: str = None, content_type: str = None): # May return dict or object datatypes """ Checks token expiration, renewing when necessary, then performs the request. """ if self.token_expired(): self.authenticate() @@ -131,13 +131,23 @@ def command(self: object, action: str = "", parameters: dict = {}, body: dict = delim = "&" if "?" in FULL_URL else "?" FULL_URL = f"{FULL_URL}{delim}file_name={str(file_name)}" HEADERS = self.headers() + if headers is None: + headers = [] for item in headers: HEADERS[item] = headers[item] if content_type: HEADERS["Content-Type"] = str(content_type) + if data is None: + data = {} DATA = data + if body is None: + body = {} BODY = body + if parameters is None: + parameters = {} PARAMS = parameters + if files is None: + files = [] FILES = files if self.authenticated: METHOD = CMD[0][1].upper() From 000c8e73f046cfc693bb501e6ee4704368cb2a12 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Mon, 12 Apr 2021 02:02:04 -0400 Subject: [PATCH 26/29] Update CHANGELOG.md --- CHANGELOG.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58951579f..2a4745b35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,13 +33,19 @@ * Falcon Complete Dashboard API * Falcon Overwatch Dashboard API * Falcon Flight Control API - +## Issues resolved ++ Fixed unidiomatic type check in `_util.py` (_parse_id_list_) ++ Fixed potentially problematic default payload lists and dictionaries (Service Classes and Uber Class) ## Other -+ Added CHANGELOG ++ Added CHANGELOG.md + Documentation updates to reflect new service class and upcoming API additions + Minor comment updates + Adjusted GitHub actions to test operating systems as separate workflows + Minor GitHub workflow adjustments ++ Unit test updates + - Cloud Connect AWS + - CSPM Registration + - Sensor Download # Version 0.4.4 ## Added features and functionality From 868ba20a8afb21fcaef965e670808e5b5923bf82 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Mon, 12 Apr 2021 02:02:21 -0400 Subject: [PATCH 27/29] Unit test adjustments to cover new code paths --- tests/test_cloud_connect_aws.py | 2 +- tests/test_cspm_registration.py | 6 +++--- tests/test_custom_ioa.py | 12 ++++++------ tests/test_sensor_download.py | 19 ++++++++++++++++++- 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/tests/test_cloud_connect_aws.py b/tests/test_cloud_connect_aws.py index 05edef52f..8a79d04d0 100644 --- a/tests/test_cloud_connect_aws.py +++ b/tests/test_cloud_connect_aws.py @@ -177,7 +177,7 @@ def serviceCCAWS_GenerateErrors(self): errorChecks = False if falcon.CreateOrUpdateAWSSettings(body={})["status_code"] != 500: errorChecks = False - if falcon.VerifyAWSAccountAccess(ids="1234567890", body={})["status_code"] != 500: + if falcon.VerifyAWSAccountAccess(ids="1234567890")["status_code"] != 500: errorChecks = False return errorChecks diff --git a/tests/test_cspm_registration.py b/tests/test_cspm_registration.py index 1c7a10ce3..141c695f3 100644 --- a/tests/test_cspm_registration.py +++ b/tests/test_cspm_registration.py @@ -56,12 +56,12 @@ def serviceStream_GenerateErrors(self): ["GetCSPMAzureAccount", "ids='12345678'"], ["CreateCSPMAzureAccount", "body={}"], ["DeleteCSPMAzureAccount", "ids='12345678'"], - ["UpdateCSPMAzureAccountClientID", "parameters={}, body={}"], + ["UpdateCSPMAzureAccountClientID", ""], # ["GetCSPMAzureUserScriptsAttachment", ""], ["GetCSPMPolicy", "ids='12345678'"], - ["GetCSPMPolicySettings", "parameters={}"], + ["GetCSPMPolicySettings", ""], ["UpdateCSPMPolicySettings", "body={}"], - ["GetCSPMScanSchedule", "parameters={}"], + ["GetCSPMScanSchedule", ""], ["UpdateCSPMScanSchedule", "body={}"] ] for cmd in commandList: diff --git a/tests/test_custom_ioa.py b/tests/test_custom_ioa.py index 85733042f..33d279cc8 100644 --- a/tests/test_custom_ioa.py +++ b/tests/test_custom_ioa.py @@ -47,15 +47,15 @@ def serviceIOA_GenerateErrors(self): ["get_patterns", "ids='12345678'"], ["get_platformsMixin0", "ids='12345678'"], ["get_rule_groupsMixin0", "ids='12345678'"], - ["create_rule_groupMixin0", "body={}, cs_username='unit_testing'"], - ["delete_rule_groupMixin0", "ids='12345678', cs_username='unit_testing'"], - ["update_rule_groupMixin0", "body={}, cs_username='unit_testing'"], + ["create_rule_groupMixin0", "body={}, cs_username='falconpy_unit_testing'"], + ["delete_rule_groupMixin0", "ids='12345678', cs_username='falconpy_unit_testing'"], + ["update_rule_groupMixin0", "body={}, cs_username='falconpy_unit_testing'"], ["get_rule_types", "ids='12345678'"], ["get_rules_get", "ids='12345678'"], ["get_rulesMixin0", "ids='12345678'"], - ["create_rule", "body={}, cs_username='unit_testing'"], - ["delete_rules", "ids='12345678', parameters={}, cs_username='unit_testing'"], - ["update_rules", "body={}, cs_username='unit_testing'"], + ["create_rule", "body={}, cs_username='falconpy_unit_testing'"], + ["delete_rules", "ids='12345678', cs_username='falconpy_unit_testing'"], + ["update_rules", "body={}, cs_username='falconpy_unit_testing'"], ["validate", "body={}"], ["query_patterns", ""], ["query_platformsMixin0", ""], diff --git a/tests/test_sensor_download.py b/tests/test_sensor_download.py index cbf2a9f8e..58806ff14 100644 --- a/tests/test_sensor_download.py +++ b/tests/test_sensor_download.py @@ -12,8 +12,9 @@ sensor_download_client = FalconSensorDownload.Sensor_Download(access_token=auth.token) + class TestSensorDownload(): - + """Sensor Download unit test series""" @staticmethod def _get_cid(): resp = sensor_download_client.GetSensorInstallersCCIDByQuery() @@ -55,6 +56,16 @@ def _get_metadata_for_ids(self): resp = sensor_download_client.GetSensorInstallersEntities(ids=sha_ids) return True if resp["status_code"] in AllowedResponses else False + @staticmethod + def _get_all_metadata(): + resp = sensor_download_client.GetCombinedSensorInstallersByQuery() + return True if resp["status_code"] in AllowedResponses else False + + @staticmethod + def _get_all_metadata2(): + resp = sensor_download_client.GetSensorInstallersByQuery() + return True if resp["status_code"] in AllowedResponses else False + def test_download_windows_sensor(self): assert self._download_sensor() is True @@ -72,3 +83,9 @@ def test_get_shas(self): def test_get_mutliple_shas(self): assert self._get_metadata_for_ids() is True + + def test_get_all_metadata(self): + assert self._get_all_metadata() is True + + def test_get_all_metadata2(self): + assert self._get_all_metadata2() is True \ No newline at end of file From 944910c171ef79253b21d143845f60580a554f3f Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Mon, 12 Apr 2021 02:13:06 -0400 Subject: [PATCH 28/29] Flake8 is unhappy with the complexity of the command method --- src/falconpy/api_complete.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/falconpy/api_complete.py b/src/falconpy/api_complete.py index 5a9ba66b4..9eeffd466 100644 --- a/src/falconpy/api_complete.py +++ b/src/falconpy/api_complete.py @@ -107,9 +107,9 @@ def deauthenticate(self: object) -> bool: # NOTE: Not specifying datatypes for "ids" and "partition" parameters # to allow developers to pass str / lists / integers as necessary - def command(self: object, action: str = "", parameters: dict = None, body: dict = None, data: dict = None, + def command(self: object, action: str = "", parameters: dict = None, body: dict = None, data: dict = None, # noqa: C901 headers: dict = None, ids=None, partition=None, override: str = None, action_name: str = None, - files: list = None, file_name: str = None, content_type: str = None): # May return dict or object datatypes + files: list = None, file_name: str = None, content_type: str = None): """ Checks token expiration, renewing when necessary, then performs the request. """ if self.token_expired(): self.authenticate() From 046cbcd162b427c0fc4c0d501fc6419bf72afddd Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Mon, 12 Apr 2021 02:31:35 -0400 Subject: [PATCH 29/29] Safer default list payload parameter --- src/falconpy/_util.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/falconpy/_util.py b/src/falconpy/_util.py index 94dbdaeb2..6a923988c 100644 --- a/src/falconpy/_util.py +++ b/src/falconpy/_util.py @@ -113,7 +113,7 @@ def service_request(caller: object = None, **kwargs) -> object: # May return di def perform_request(method: str = "", endpoint: str = "", headers: dict = None, params: dict = None, body: dict = None, verify: bool = True, - data=None, files: list = [], + data=None, files: list = None, params_validator: dict = None, params_required: dict = None, body_validator: dict = None, body_required: dict = None) -> object: # May return dict or object datatypes """ @@ -144,6 +144,8 @@ def perform_request(method: str = "", endpoint: str = "", headers: dict = None, body_required: list - List of payload parameters required by the requested operation - Example: ["ids"] """ + if files is None: + files = [] PERFORM = True METHOD = method.upper() if METHOD in _ALLOWED_METHODS: