diff --git a/.github/wordlist.txt b/.github/wordlist.txt index 0cbe1f29c..039a9e933 100644 --- a/.github/wordlist.txt +++ b/.github/wordlist.txt @@ -1339,3 +1339,25 @@ batchgetcmd batchgetcmdstatus deletefilev listfilesv +ArchiveUploadV +WorkflowMockExecute +WorkflowExecuteInternal +GetSensorInstallersByQueryV +GetSensorInstallersEntitiesV +DownloadSensorInstallerByIdV +GetCombinedSensorInstallersByQueryV +QueryMitreAttacksForMalware +QueryMalware +GetMalwareEntities +IngestDataAsyncV +AggregateSupportIssues +hostsV +iot +ValidateCSPMGCPServiceAccountExt +GetCSPMGCPValidateAccountsExt +DeleteCSPMAzureManagementGroup +GetRuntimeDetectionsCombinedV +GetScanReport +CreateDeploymentEntity +ReadDeploymentsEntities +ReadDeploymentsCombined diff --git a/.github/workflows/unit_testing_eu1.yml b/.github/workflows/unit_testing_eu1.yml index 4c76ed42e..9108e374f 100644 --- a/.github/workflows/unit_testing_eu1.yml +++ b/.github/workflows/unit_testing_eu1.yml @@ -24,7 +24,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: '3.10' + python-version: '3.9' - name: Install dependencies run: | python -m pip install --upgrade pip diff --git a/.github/workflows/unit_testing_ubuntu.yml b/.github/workflows/unit_testing_ubuntu.yml index 9629af712..1728a179b 100644 --- a/.github/workflows/unit_testing_ubuntu.yml +++ b/.github/workflows/unit_testing_ubuntu.yml @@ -20,7 +20,7 @@ jobs: strategy: matrix: # os: [macos-latest, windows-latest, ubuntu-latest] - python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13-dev'] + python-version: ['3.10', '3.11', '3.12', '3.13-dev'] # runs-on: ${{ matrix.os }} runs-on: ubuntu-latest steps: diff --git a/.github/workflows/unit_testing_us2.yml b/.github/workflows/unit_testing_us2.yml index 52c2f7d06..220b97cb0 100644 --- a/.github/workflows/unit_testing_us2.yml +++ b/.github/workflows/unit_testing_us2.yml @@ -21,7 +21,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: '3.10' + python-version: '3.8' - name: Install dependencies run: | python -m pip install --upgrade pip diff --git a/.github/workflows/unit_testing_usgov1.yml b/.github/workflows/unit_testing_usgov1.yml index 9aa33bc43..f517f6bf1 100644 --- a/.github/workflows/unit_testing_usgov1.yml +++ b/.github/workflows/unit_testing_usgov1.yml @@ -21,7 +21,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: '3.10' + python-version: '3.7' - name: Install dependencies run: | python -m pip install --upgrade pip diff --git a/AUTHORS.md b/AUTHORS.md index 421fea016..cab8e04e8 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -102,11 +102,11 @@ Without the support of these executives, the FalconPy project would not have hap | Name | Role | | :-- | :-- | | Chris Kachigian, `@ckachigian` | Herder of Cats | -| Rekha Das | Gatekeeper | | Robbie Coleman, `@erraggy` | Keymaster | | Mike Cryer | Colonel-in-Chief | #### Honorable mentions ++ Rekha Das + Jaime Franklin, `@franklinjff` + Shawn Wells, `@shawndwells` diff --git a/CHANGELOG.md b/CHANGELOG.md index ce04b1d94..98357bd6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,164 @@ +# Version 1.4.2 +## Added features and functionality ++ Expanded: Environment Authentication functionality has been expanded to allow developers to customize the names of the environment keys used to store API credentials. + - `_auth_object/_falcon_interface.py` + - `_auth_object/_uber_interface.py` + - `oauth2.py` + > Unit testing expanded to complete code coverage. + - `tests/test_authentications.py` + ```python + from falconpy import Hosts + # We can now define the prefix and the names of the + # environment values used for API key lookups + environment_keys = { + "prefix": "CROWDSTRIKE_", + "id_name": "API_ID", + "secret_name": "API_SECRET" + } + # These values are provided as a dictionary to the class + hosts = Hosts(environment=environment_keys) + # Usage of the class is the same + results = hosts.query_devices_by_filter_scroll() + ``` + ++ Added: `include_hidden` argument added to the _PostAggregatesAlertsV2_, _PatchEntitiesAlertsV3_, _PostEntitiesAlertsV2_ and _GetQueriesAlertsV2_ operations within the __Alerts__ Service Class. + - `alerts.py` + ++ Added: Added 4 new operations to the __Cloud Snapshots__ service collection. + - _ReadDeploymentsCombined_ + - _ReadDeploymentsEntities_ + - _CreateDeploymentEntity_ + - _GetScanReport_ + - `_endpoint/_cloud_snapshots.py` + - `_payload/__init__.py` + - `_payload/_cloud_snapshots.py` + - `cloud_snapshots.py` + > Unit testing expanded to complete code coverage. + - `tests/test_cloud_snapshots.py` + ++ Added: Added _GetRuntimeDetectionsCombinedV2_ to the __Container Detections__ service collection. + - `_endpoint/_container_detections.py` + - `container_detections.py` + > Unit testing expanded to complete code coverage. + - `tests/test_container_detections.py` + ++ Added: Added 3 new operations to the __CSPM Registration__ service collection. + - _DeleteCSPMAzureManagementGroup_ + - _GetCSPMGCPValidateAccountsExt_ + - _ValidateCSPMGCPServiceAccountExt_ + - `_endpoint/_cspm_registration.py` + - `_payload/__init__.py` + - `_payload/_cspm_registration.py` + - `cspm_registration.py` + > Unit testing expanded to complete code coverage. + - `tests/test_cspm_registration.py` + ++ Added: Added _query_iot_hostsV2_ operation to the __Discover__ service collection. + - `_endpoint/_discover.py` + - `_endpoint/deprecated/_discover.py` + - `discover.py` + > Unit testing expanded to complete code coverage. + - `tests/test_discover.py` + ++ Added: Added _AggregateSupportIssues_ operation to the __Falcon Complete Dashboard__ service collection. + - `_endpoint/_falcon_complete_dashboard.py` + - `falcon_complete_dashboard.py` + > Unit testing expanded to complete code coverage. + - `tests/test_falcon_complete_dashboard.py` + ++ Added: Added _IngestDataAsyncV1_ operation to the __Foundry LogScale__ service collection. + - `_endpoint/_foundry_logscale.py` + - `foundry_logscale.py` + > Unit testing expanded to complete code coverage. + - `tests/test_foundry_logscale.py` + ++ Added: Added `infer_json_types` and `match_response_schema` arguments to the _CreateSavedSearchesDynamicExecuteV1_, _GetSavedSearchesExecuteV1_ and _CreateSavedSearchesExecuteV1_ operations within the __Foundry LogScale__ service collection. + - `_endpoint/_foundry_logscale.py` + - `foundry_logscale.py` + ++ Added: Added `infer_json_types` argument to the _GetSavedSearchesJobResultsDownloadV1_ operation within the __Foundry LogScale__ service collection. + - `_endpoint/_foundry_logscale.py` + - `foundry_logscale.py` + ++ Added: Added 3 new operations to the __Intel__ service collection. + - _GetMalwareEntities_ + - _QueryMalware_ + - _QueryMitreAttacksForMalware_ + - `_endpoint/_intel.py` + - `intel.py` + > Unit testing expanded to complete code coverage. + - `tests/test_intel.py` + ++ Added: Added 4 new operations to the __Sensor Download__ service collection. + - _GetCombinedSensorInstallersByQueryV2_ + - _DownloadSensorInstallerByIdV2_ + - _GetSensorInstallersEntitiesV2_ + - _GetSensorInstallersByQueryV2_ + - `_endpoint/_sensor_download.py` + - `sensor_download.py` + > Unit testing expanded to complete code coverage. + - `tests/test_sensor_download.py` + ++ Added: Added `sanitize` argument to the _WorkflowDefinitionsExport_ operation within the __Workflows__ service collection. + - `_endpoint/_workflows.py` + - `workflows.py` + ++ Added: Added 2 new operations to the __Workflows__ service collection. + - _WorkflowExecuteInternal_ + - _WorkflowMockExecute_ + - `_endpoint/workflows.py` + - `_payload/__init__.py` + - `_payload/_workflows.py` + - `workflows.py` + > Unit testing expanded to complete code coverage. + - `tests/test_workflows.py` + +## Issue resolved ++ Fixed: Resolved parsing issue with formData arguments provided to the _ArchiveUploadV2_ operation within the __SampleUploads__ Service Class. Closes #1122. + - `sample_uploads.py` + ++ Fixed: Resolved conversion issue with query string boolean parameters not being properly converted to lowercase before API submission. Closes #1129. + - `_util/_functions.py` + +## Other ++ Updated: Updated `body` argument description for the _PatchEntitiesAlertsV3_ operation within the endpoint module. + - `_endpoint/_alerts.py` + ++ Updated: Added `highest_cps_current_rating` as an allowed sort parameter to the _ReadCombinedImagesExport_ operation within the __Container Images__ service collection. + - `_endpoint/_container_images.py` + ++ Updated: Added `watch_permissions_key_changes` option to the _createRules_ operation within the __FileVantage__ service collection. + - `_endpoint/_filevantage.py` + ++ Updated: Updated operation and argument descriptions in the deprecated __IOCS__ service collection. + - `_endpoint/_iocs.py` + ++ Updated: Added `prevented` as an allowed filter to the _ReadKubernetesIomByDateRange_, _ReadKubernetesIomCount_, _SearchAndReadKubernetesIomEntities_ and _SearchKubernetesIoms_ operations within the __Kubernetes Protection__ service collection. + - `_endpoint/_kubernetes_protection.py` + ++ Updated: Updated the `body` argument description for the _BatchAdminCmd_ and _RTR_ExecuteAdminCommand_ operations within the __Real Time Response Admin__ service collection. + - `_endpoint/_real_time_response_admin.py` + - `_endpoint/deprecated/_real_time_response_admin.py` + ++ Updated: Updated the `body` argument description for the _BatchActiveResponderCmd_, _BatchCmd_, _RTR_ExecuteActiveResponderCommand_, and _RTR_ExecuteCommand_ operations within the __Real Time Response__ service collection. + - `_endpoint/_real_time_response.py` + - `_endpoint/deprecated/_real_time_response.py` + ++ Removed: The _CreateInventory_ operation is removed from the __Cloud Snapshots__ Service Class. + - `_payload/__init__.py` + - `_payload/_cloud_snapshots.py` + - `cloud_snapshots.py` + > Unit testing updated to reflect current functionality. + - `tests/test_cloud_snapshots.py` + ++ Removed: The _WorkflowDefinitionsCreate_ operation is removed from the __Workflows__ service collection. + - `_endpoint/_workflows.py` + - `workflows.py` + > Unit testing updated to reflect current functionality. + - `tests/test_workflows.py` + +--- + # Version 1.4.1 ## Added features and functionality + Added: `include_hidden` argument added to the _PostAggregatesAlertsV2_, _PostEntitiesAlertsV2_, _PatchEntitiesAlertsV3_ and _GetQueriesAlertsV2_ operations. diff --git a/README.md b/README.md index 633ac44ef..1e9d709a5 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ [![Release date](https://img.shields.io/github/release-date/CrowdStrike/falconpy)](https://github.com/CrowdStrike/falconpy/releases) [![Repo status](https://img.shields.io/osslifecycle/crowdstrike/falconpy?label=repo%20status)](https://github.com/CrowdStrike/falconpy/graphs/code-frequency) [![Commit activity](https://img.shields.io/github/commits-since/CrowdStrike/falconpy/latest)](https://github.com/CrowdStrike/falconpy/commits/main) -![GitHub forks](https://img.shields.io/github/forks/crowdstrike/falconpy) +![GitHub forks](https://img.shields.io/github/forks/crowdstrike/falconpy?style=flat) The FalconPy SDK contains a collection of Python classes that abstract CrowdStrike Falcon OAuth2 API interaction, removing duplicative code and allowing developers to focus on just the logic of their solution requirements. @@ -83,6 +83,7 @@ For each CrowdStrike Falcon API service collection, a matching Service Class is - Closely follows Python and OpenAPI best practice for code style and syntax. PEP-8 compliant. - Completely abstracts token management, automatically refreshing your token when it expires. +- Interact with newly released API operations not yet available in the library via the `override` method. - Provides simple programmatic patterns for interacting with CrowdStrike Falcon APIs. - Supports [cloud region autodiscovery](https://www.falconpy.io/Usage/Environment-Configuration.html#cloud-region-autodiscovery) for the CrowdStrike `US-1`, `US-2` and `EU-1` regions. - Supports dynamic [configuration](https://www.falconpy.io/Usage/Environment-Configuration.html) based upon the needs of your environment. diff --git a/src/falconpy/_auth_object/_falcon_interface.py b/src/falconpy/_auth_object/_falcon_interface.py index 731413acd..f6f60f717 100644 --- a/src/falconpy/_auth_object/_falcon_interface.py +++ b/src/falconpy/_auth_object/_falcon_interface.py @@ -90,7 +90,8 @@ def __init__(self, debug: Optional[bool] = False, debug_record_count: Optional[int] = None, sanitize_log: Optional[bool] = None, - pythonic: Optional[bool] = False + pythonic: Optional[bool] = False, + environment: Optional[Dict[str, str]] = None ) -> "FalconInterface": """Construct an instance of the FalconInterface class.""" # Set the pythonic behavior mode. @@ -149,15 +150,16 @@ def __init__(self, ) # Environment Authentication + # User configuration environment keys + self._environment = environment if environment else {} # When credentials are not provided, attempt to retrieve them from the environment. if not self.cred_format_valid and not self.token_value: # Both variables must be present within the running environment. - if os.getenv("FALCON_CLIENT_ID") and os.getenv("FALCON_CLIENT_SECRET"): - api_id = os.getenv("FALCON_CLIENT_ID") if "client_id" not in self.creds else self.creds["client_id"] - if "client_secret" not in self.creds: - api_sec = os.getenv("FALCON_CLIENT_SECRET") - else: - api_sec = self.creds["client_secret"] + if os.getenv(f"{self.env_prefix}{self.env_key}") and os.getenv(f"{self.env_prefix}{self.env_secret}"): + api_id = os.getenv(f"{self.env_prefix}{self.env_key}") \ + if "client_id" not in self.creds else self.creds["client_id"] + api_sec = os.getenv(f"{self.env_prefix}{self.env_secret}") \ + if "client_secret" not in self.creds else self.creds["client_secret"] # Environment Authentication will not override values that preexist in the creds dictionary. self._creds = { "client_id": api_id, @@ -511,3 +513,18 @@ def pythonic(self) -> bool: def pythonic(self, value: bool): """Enable or disable pythonic mode.""" self._pythonic = value + + @property + def env_prefix(self) -> str: + """Return the environment prefix.""" + return self._environment.get("prefix", "FALCON_") + + @property + def env_key(self) -> str: + """Return the environment API key name.""" + return self._environment.get("id_name", "CLIENT_ID") + + @property + def env_secret(self) -> str: + """Return the environment API key secret.""" + return self._environment.get("secret_name", "CLIENT_SECRET") diff --git a/src/falconpy/_auth_object/_uber_interface.py b/src/falconpy/_auth_object/_uber_interface.py index b23e61b11..975ddeb08 100644 --- a/src/falconpy/_auth_object/_uber_interface.py +++ b/src/falconpy/_auth_object/_uber_interface.py @@ -77,7 +77,8 @@ def __init__(self, debug: Optional[bool] = False, debug_record_count: Optional[int] = MAX_DEBUG_RECORDS, sanitize_log: Optional[bool] = None, - pythonic: Optional[bool] = None + pythonic: Optional[bool] = None, + environment: Optional[Dict[str, str]] = None ): """Construct an instance of the UberInterface class. @@ -126,7 +127,8 @@ def __init__(self, debug=debug, debug_record_count=debug_record_count, sanitize_log=sanitize_log, - pythonic=pythonic + pythonic=pythonic, + environment=environment ) # Complete list of available API operations. diff --git a/src/falconpy/_endpoint/_alerts.py b/src/falconpy/_endpoint/_alerts.py index fc2c388f5..1239779b7 100644 --- a/src/falconpy/_endpoint/_alerts.py +++ b/src/falconpy/_endpoint/_alerts.py @@ -169,7 +169,8 @@ "in": "query" }, { - "description": "request body takes a list of action parameter request that is applied against all \"ids\" provided", + "description": "request body takes a list of action parameter request that is applied against all " + "\"composite_ids\" provided", "name": "body", "in": "body", "required": True diff --git a/src/falconpy/_endpoint/_cloud_snapshots.py b/src/falconpy/_endpoint/_cloud_snapshots.py index 1666d4506..79177adc1 100644 --- a/src/falconpy/_endpoint/_cloud_snapshots.py +++ b/src/falconpy/_endpoint/_cloud_snapshots.py @@ -37,6 +37,41 @@ """ _cloud_snapshots_endpoints = [ + [ + "ReadDeploymentsCombined", + "GET", + "/snapshots/combined/deployments/v1", + "Retrieve snapshot jobs identified by the provided IDs", + "cloud_snapshots", + [ + { + "type": "string", + "description": "Search snapshot jobs using a query in Falcon Query Language (FQL). Supported filters: " + "account_id,asset_identifier,cloud_provider,region,status", + "name": "filter", + "in": "query" + }, + { + "type": "integer", + "description": "The upper-bound on the number of records to retrieve.", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "The offset from where to begin.", + "name": "offset", + "in": "query" + }, + { + "type": "string", + "description": "The fields to sort the records on. Supported columns: [account_id asset_identifier " + "cloud_provider instance_type last_updated_timestamp region status]", + "name": "sort", + "in": "query" + } + ] + ], [ "RegisterCspmSnapshotAccount", "POST", @@ -51,6 +86,39 @@ } ] ], + [ + "ReadDeploymentsEntities", + "GET", + "/snapshots/entities/deployments/v1", + "Retrieve snapshot jobs identified by the provided IDs", + "cloud_snapshots", + [ + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "Search snapshot jobs by ids - The maximum amount is 100 IDs", + "name": "ids", + "in": "query" + } + ] + ], + [ + "CreateDeploymentEntity", + "POST", + "/snapshots/entities/deployments/v1", + "Launch a snapshot scan for a given cloud asset", + "cloud_snapshots", + [ + { + "name": "body", + "in": "body", + "required": True + } + ] + ], [ "GetCredentialsMixin0", "GET", @@ -72,5 +140,25 @@ "required": True } ] + ], + [ + "GetScanReport", + "GET", + "/snapshots/entities/scanreports/v1", + "retrieve the scan report for an instance", + "cloud_snapshots", + [ + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "the instance identifiers to fetch the report for", + "name": "ids", + "in": "query", + "required": True + } + ] ] ] diff --git a/src/falconpy/_endpoint/_container_detections.py b/src/falconpy/_endpoint/_container_detections.py index 254abaae6..909dd9cfe 100644 --- a/src/falconpy/_endpoint/_container_detections.py +++ b/src/falconpy/_endpoint/_container_detections.py @@ -120,6 +120,42 @@ } ] ], + [ + "GetRuntimeDetectionsCombinedV2", + "GET", + "/container-security/combined/runtime-detections/v2", + "Retrieve container runtime detections by the provided search criteria", + "container_detections", + [ + { + "type": "string", + "description": "Filter Container Runtime Detections using a query in Falcon Query Language (FQL). " + "Supported filters: action_taken,aid,cid,cloud,cluster_name,command_line,computer_name,container_id,detect_tim" + "estamp,detection_description,detection_id,file_name,file_path,host_id,host_type,image_id,name,namespace,pod_na" + "me,severity,tactic", + "name": "filter", + "in": "query" + }, + { + "type": "integer", + "description": "The upper-bound on the number of records to retrieve.", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "The offset from where to begin.", + "name": "offset", + "in": "query" + }, + { + "type": "string", + "description": "The field to sort the records on.", + "name": "sort", + "in": "query" + } + ] + ], [ "ReadDetections", "GET", diff --git a/src/falconpy/_endpoint/_container_images.py b/src/falconpy/_endpoint/_container_images.py index 2b3440558..bbb665478 100644 --- a/src/falconpy/_endpoint/_container_images.py +++ b/src/falconpy/_endpoint/_container_images.py @@ -250,8 +250,9 @@ { "type": "string", "description": "The fields to sort the records on. Supported columns: [base_os cid containers " - "detections firstScanned first_seen highest_detection_severity highest_vulnerability_severity image_digest " - "image_id last_seen layers_with_vulnerabilities packages registry repository tag vulnerabilities]", + "detections firstScanned first_seen highest_cps_current_rating highest_detection_severity " + "highest_vulnerability_severity image_digest image_id last_seen layers_with_vulnerabilities packages registry " + "repository tag vulnerabilities]", "name": "sort", "in": "query" } diff --git a/src/falconpy/_endpoint/_cspm_registration.py b/src/falconpy/_endpoint/_cspm_registration.py index afb35ba74..56abd5368 100644 --- a/src/falconpy/_endpoint/_cspm_registration.py +++ b/src/falconpy/_endpoint/_cspm_registration.py @@ -533,6 +533,25 @@ } ] ], + [ + "DeleteCSPMAzureManagementGroup", + "DELETE", + "/cloud-connect-cspm-azure/entities/management-group/v1", + "Deletes Azure management groups from the system.", + "cspm_registration", + [ + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", + "description": "Tenant ids to remove", + "name": "tenant_ids", + "in": "query" + } + ] + ], [ "GetCSPMAzureUserScriptsAttachment", "GET", @@ -718,6 +737,20 @@ } ] ], + [ + "GetCSPMGCPValidateAccountsExt", + "POST", + "/cloud-connect-cspm-gcp/entities/account/validate/v1", + "Run a synchronous health check.", + "cspm_registration", + [ + { + "name": "body", + "in": "body", + "required": True + } + ] + ], [ "GetCSPMGCPServiceAccountsExt", "GET", @@ -734,6 +767,20 @@ } ] ], + [ + "ValidateCSPMGCPServiceAccountExt", + "POST", + "/cloud-connect-cspm-gcp/entities/service-accounts/validate/v1", + "Validates credentials for a service account", + "cspm_registration", + [ + { + "name": "body", + "in": "body", + "required": True + } + ] + ], [ "GetCSPMGCPUserScriptsAttachment", "GET", @@ -944,8 +991,8 @@ "GetConfigurationDetections", "GET", "/detects/entities/iom/v1", - "Get list of active misconfigurations. This endpoint is deprecated, please use GetConfigurationDetectionIDsV2 and " - "GetConfigurationDetectionEntities instead", + "Get list of active misconfigurations. This endpoint is deprecated, please use " + "GetConfigurationDetectionIDsV2 and GetConfigurationDetectionEntities instead", "cspm_registration", [ { diff --git a/src/falconpy/_endpoint/_discover.py b/src/falconpy/_endpoint/_discover.py index 0d363469d..6149743a1 100644 --- a/src/falconpy/_endpoint/_discover.py +++ b/src/falconpy/_endpoint/_discover.py @@ -384,6 +384,54 @@ } ] ], + [ + "query_iot_hostsV2", + "GET", + "/discover/queries/iot-hosts/v2", + "Search for IoT assets in your environment by providing an FQL (Falcon Query Language) filter and paging " + "details. Returns a set of asset IDs which match the filter criteria.", + "discover", + [ + { + "type": "string", + "description": "A pagination token used with the `limit` parameter to manage pagination of results. On " + " your first request, don't provide an `after` token. On subsequent requests, provide the `after` token from " + "the previous response to continue from that place in the results.", + "name": "after", + "in": "query" + }, + { + "maximum": 100, + "minimum": 1, + "type": "integer", + "description": "The number of asset IDs to return in this response (min: 1, max: 100, default: 100). " + "Use with the `after` parameter to manage pagination of results.", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "Sort assets by their properties. A single sort field is allowed. Common sort options " + "include:\n\n", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "description": "Filter assets using an FQL query. Common filter options include:\n\t\t\tAvailable filter fields that support exact match: device_family, device_class, " + "device_type, device_mode, business_criticality, line_of_business, virtual_zone, subnet, purdue_level, vlan, " + "local_ip_addresses, mac_addresses, physical_connections_count, data_providers\n\t\t\tAvailable filter fields " + "that supports wildcard (*): device_family, device_class, device_type, device_mode, business_criticality, " + "line_of_business, virtual_zone, subnet, purdue_level, vlan, local_ip_addresses, mac_addresses, " + "data_providers\n\t\t\tAvailable filter fields that supports range comparisons (>, <, >=, <=): " + "physical_connections_count\n\t\t\tAll filter fields and operations supports negation (!).", + "name": "filter", + "in": "query" + } + ] + ], [ "query_logins", "GET", diff --git a/src/falconpy/_endpoint/_falcon_complete_dashboard.py b/src/falconpy/_endpoint/_falcon_complete_dashboard.py index 72713257e..e46a868c2 100644 --- a/src/falconpy/_endpoint/_falcon_complete_dashboard.py +++ b/src/falconpy/_endpoint/_falcon_complete_dashboard.py @@ -177,6 +177,20 @@ } ] ], + [ + "AggregateSupportIssues", + "POST", + "/falcon-complete-dashboards/aggregates/support-issues/v1", + "Retrieve aggregate support issue ticket values based on the matched filter", + "falcon_complete_dashboard", + [ + { + "name": "body", + "in": "body", + "required": True + } + ] + ], [ "AggregateTotalDeviceCounts", "POST", diff --git a/src/falconpy/_endpoint/_filevantage.py b/src/falconpy/_endpoint/_filevantage.py index 671ffbacb..fbeff9965 100644 --- a/src/falconpy/_endpoint/_filevantage.py +++ b/src/falconpy/_endpoint/_filevantage.py @@ -454,7 +454,7 @@ "is not supported at this time)\n\n * `watch_permissions_file_changes` (`macOS` is not supported at this " "time)\n\nWindows registry key and value monitoring: \n\n * `watch_create_key_changes`\n\n * " "`watch_delete_key_changes`\n\n * `watch_rename_key_changes`\n\n * `watch_set_value_changes`\n\n * " - "`watch_delete_value_changes`\n\n * `watch_create_file_changes`", + "`watch_permissions_key_changes`\n\n * `watch_delete_value_changes`\n\n * `watch_create_file_changes`", "name": "body", "in": "body", "required": True diff --git a/src/falconpy/_endpoint/_foundry_logscale.py b/src/falconpy/_endpoint/_foundry_logscale.py index 7b0f7d6e9..0dde74f7d 100644 --- a/src/falconpy/_endpoint/_foundry_logscale.py +++ b/src/falconpy/_endpoint/_foundry_logscale.py @@ -53,11 +53,55 @@ } ] ], + [ + "IngestDataAsyncV1", + "POST", + "/loggingapi/entities/data-ingestion/ingest-async/v1", + "Asynchronously ingest data into the application repository", + "foundry_logscale", + [ + { + "type": "file", + "description": "Data file to ingest", + "name": "data_file", + "in": "formData", + "required": True + }, + { + "type": "string", + "description": "Repository name if not part of a foundry app", + "name": "repo", + "in": "formData" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "description": "Custom tag for ingested data in the form tag:value", + "name": "tag", + "in": "formData" + }, + { + "type": "string", + "description": "Tag the data with the specified source", + "name": "tag_source", + "in": "formData" + }, + { + "type": "boolean", + "default": False, + "description": "Tag the data with test-ingest", + "name": "test_data", + "in": "formData" + } + ] + ], [ "IngestDataV1", "POST", "/loggingapi/entities/data-ingestion/ingest/v1", - "Ingest data into the application repository", + "Synchronously ingest data into the application repository", "foundry_logscale", [ { @@ -118,6 +162,20 @@ "name": "include_test_data", "in": "query" }, + { + "type": "boolean", + "default": False, + "description": "Whether to try to infer data types in json event response instead of returning map[string]string", + "name": "infer_json_types", + "in": "query" + }, + { + "type": "boolean", + "default": False, + "description": "Whether to validate search results against their schema", + "name": "match_response_schema", + "in": "query" + }, { "type": "boolean", "default": False, @@ -162,6 +220,13 @@ "name": "app_id", "in": "query" }, + { + "type": "boolean", + "default": False, + "description": "Whether to try to infer data types in json event response instead of returning map[string]string", + "name": "infer_json_types", + "in": "query" + }, { "minimum": 0, "type": "string", @@ -169,6 +234,13 @@ "name": "limit", "in": "query" }, + { + "type": "boolean", + "default": False, + "description": "Whether to validate search results against their schema", + "name": "match_response_schema", + "in": "query" + }, { "type": "boolean", "default": False, @@ -212,6 +284,20 @@ "name": "include_test_data", "in": "query" }, + { + "type": "boolean", + "default": False, + "description": "Whether to try to infer data types in json event response instead of returning map[string]string", + "name": "infer_json_types", + "in": "query" + }, + { + "type": "boolean", + "default": False, + "description": "Whether to validate search results against their schema", + "name": "match_response_schema", + "in": "query" + }, { "type": "boolean", "default": False, @@ -255,6 +341,13 @@ "in": "query", "required": True }, + { + "type": "boolean", + "default": False, + "description": "Whether to try to infer data types in json event response instead of returning map[string]string", + "name": "infer_json_types", + "in": "query" + }, { "enum": [ "json", diff --git a/src/falconpy/_endpoint/_intel.py b/src/falconpy/_endpoint/_intel.py index 69f3e50f5..89f7a17e6 100644 --- a/src/falconpy/_endpoint/_intel.py +++ b/src/falconpy/_endpoint/_intel.py @@ -258,6 +258,26 @@ } ] ], + [ + "GetMalwareEntities", + "GET", + "/intel/entities/malware/v1", + "Get malware entities for specified ids.", + "intel", + [ + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", + "description": "Malware family name in lower case with spaces, dots and slashes replaced with dashes", + "name": "ids", + "in": "query", + "required": True + } + ] + ], [ "GetMitreReport", "GET", @@ -560,6 +580,65 @@ } ] ], + [ + "QueryMalware", + "GET", + "/intel/queries/malware/v1", + "Get malware family names that match provided FQL filters.", + "intel", + [ + { + "type": "integer", + "description": "Set the starting row number to return malware IDs from. Defaults to 0.", + "name": "offset", + "in": "query" + }, + { + "type": "integer", + "description": "Set the number of malware IDs to return. The value must be between 1 and 5000.", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "Order fields in ascending or descending order.\n\nEx: created_date|asc.", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "description": "Filter your query by specifying FQL filter parameters.", + "name": "filter", + "in": "query" + }, + { + "type": "string", + "description": "Perform a generic substring search across all fields.", + "name": "q", + "in": "query" + } + ] + ], + [ + "QueryMitreAttacksForMalware", + "GET", + "/intel/queries/mitre-malware/v1", + "Gets MITRE tactics and techniques for the given malware", + "intel", + [ + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", + "description": "Malware family name in lower case with spaces replaced with dashes", + "name": "ids", + "in": "query", + "required": True + } + ] + ], [ "QueryMitreAttacks", "GET", diff --git a/src/falconpy/_endpoint/_iocs.py b/src/falconpy/_endpoint/_iocs.py index c32804ee9..b0b2a1326 100644 --- a/src/falconpy/_endpoint/_iocs.py +++ b/src/falconpy/_endpoint/_iocs.py @@ -46,11 +46,10 @@ [ { "type": "string", - "description": "\nThe type of the indicator. Valid types include:\n\nsha256: " - "A hex-encoded sha256 hash string. Length - min: 64, max: 64.\n\nmd5: A hex-encoded " - "md5 hash string. Length - min 32, max: 32.\n\ndomain: A domain name. Length - min: 1, " - "max: 200.\n\nipv4: An IPv4 address. Must be a valid IP address.\n\nipv6: An IPv6 address. " - "Must be a valid IP address.\n", + "description": "\nThe type of the indicator. Valid types include:\n\nsha256: A hex-encoded sha256 hash " + " string. Length - min: 64, max: 64.\n\nmd5: A hex-encoded md5 hash string. Length - min 32, max: " + "32.\n\ndomain: A domain name. Length - min: 1, max: 200.\n\nipv4: An IPv4 address. Must be a valid IP " + "address.\n\nipv6: An IPv6 address. Must be a valid IP address.\n", "name": "type", "in": "query", "required": True @@ -68,17 +67,16 @@ "GetIOC", "GET", "/indicators/entities/iocs/v1", - "Get an IOC by providing a type and value. " - "*** Deprecated - Use the new IOC Management endpoint (GET /iocs/entities/indicators/v1). ***", + "Get an IOC by providing a type and value. *** Deprecated - Use the new IOC Management endpoint (GET " + "/iocs/entities/indicators/v1). ***", "iocs", [ { "type": "string", - "description": "\nThe type of the indicator. Valid types include:\n\nsha256: " - "A hex-encoded sha256 hash string. Length - min: 64, max: 64.\n\nmd5: A " - "hex-encoded md5 hash string. Length - min 32, max: 32.\n\ndomain: A domain name. " - "Length - min: 1, max: 200.\n\nipv4: An IPv4 address. Must be a valid IP address.\n\nipv6: " - "An IPv6 address. Must be a valid IP address.\n", + "description": "\nThe type of the indicator. Valid types include:\n\nsha256: A hex-encoded sha256 hash " + " string. Length - min: 64, max: 64.\n\nmd5: A hex-encoded md5 hash string. Length - min 32, max: " + "32.\n\ndomain: A domain name. Length - min: 1, max: 200.\n\nipv4: An IPv4 address. Must be a valid IP " + "address.\n\nipv6: An IPv6 address. Must be a valid IP address.\n", "name": "type", "in": "query", "required": True @@ -100,18 +98,18 @@ "iocs", [ { - "description": "Create a new IOC by providing a JSON object that includes these key/value pairs:\n\n" - "**type** (required): The type of the indicator. Valid values:\n\n- sha256: A hex-encoded sha256 hash " - "string. Length - min: 64, max: 64.\n\n- md5: A hex-encoded md5 hash string. Length - min 32, max: 32.\n\n" - "- domain: A domain name. Length - min: 1, max: 200.\n\n- ipv4: An IPv4 address. Must be a valid IP address.\n\n" - "- ipv6: An IPv6 address. Must be a valid IP address.\n\n**value** (required): The string representation of the " + "description": "Create a new IOC by providing a JSON object that includes these key/value " + "pairs:\n\n**type** (required): The type of the indicator. Valid values:\n\n- sha256: A hex-encoded sha256 hash " + " string. Length - min: 64, max: 64.\n\n- md5: A hex-encoded md5 hash string. Length - min 32, max: 32.\n\n- " + "domain: A domain name. Length - min: 1, max: 200.\n\n- ipv4: An IPv4 address. Must be a valid IP address.\n\n-" + " ipv6: An IPv6 address. Must be a valid IP address.\n\n**value** (required): The string representation of the " "indicator.\n\n**policy** (required): Action to take when a host observes the custom IOC. Values:\n\n- detect: " "Enable detections for this custom IOC\n\n- none: Disable detections for this custom IOC\n\n**share_level** " "(optional): Visibility of this custom IOC. All custom IOCs are visible only within your customer account, so " "only one value is valid:\n\n- red\n\n**expiration_days** (optional): Number of days this custom IOC is active. " - "Only applies for the types `domain`, `ipv4`, and `ipv6`.\n\n**source** (optional): The source where this indicator " - "originated. This can be used for tracking where this indicator was defined. Limit 200 characters.\n\n" - "**description** (optional): Descriptive label for this custom IOC", + " Only applies for the types `domain`, `ipv4`, and `ipv6`.\n\n**source** (optional): The source where this " + "indicator originated. This can be used for tracking where this indicator was defined. Limit 200 " + "characters.\n\n**description** (optional): Descriptive label for this custom IOC", "name": "body", "in": "body", "required": True @@ -122,8 +120,8 @@ "UpdateIOC", "PATCH", "/indicators/entities/iocs/v1", - "Update an IOC by providing a type and value. " - "*** Deprecated - Use the new IOC Management endpoint (PATCH /iocs/entities/indicators/v1). ***", + "Update an IOC by providing a type and value. *** Deprecated - Use the new IOC Management endpoint (PATCH " + "/iocs/entities/indicators/v1). ***", "iocs", [ { @@ -133,10 +131,10 @@ }, { "type": "string", - "description": "\nThe type of the indicator. Valid types include:\n\nsha256: A hex-encoded sha256 hash string. " - "Length - min: 64, max: 64.\n\nmd5: A hex-encoded md5 hash string. Length - min 32, max: 32.\n\n" - "domain: A domain name. Length - min: 1, max: 200.\n\nipv4: An IPv4 address. Must be a valid IP address.\n\n" - "ipv6: An IPv6 address. Must be a valid IP address.\n", + "description": "\nThe type of the indicator. Valid types include:\n\nsha256: A hex-encoded sha256 hash " + " string. Length - min: 64, max: 64.\n\nmd5: A hex-encoded md5 hash string. Length - min 32, max: " + "32.\n\ndomain: A domain name. Length - min: 1, max: 200.\n\nipv4: An IPv4 address. Must be a valid IP " + "address.\n\nipv6: An IPv6 address. Must be a valid IP address.\n", "name": "type", "in": "query", "required": True @@ -154,16 +152,16 @@ "DeleteIOC", "DELETE", "/indicators/entities/iocs/v1", - "Delete an IOC by providing a type and value. " - "*** Deprecated - Use the new IOC Management endpoint (DELETE /iocs/entities/indicators/v1). ***", + "Delete an IOC by providing a type and value. *** Deprecated - Use the new IOC Management endpoint (DELETE " + "/iocs/entities/indicators/v1). ***", "iocs", [ { "type": "string", - "description": "\nThe type of the indicator. Valid types include:\n\nsha256: A hex-encoded sha256 hash string. " - "Length - min: 64, max: 64.\n\nmd5: A hex-encoded md5 hash string. Length - min 32, max: 32.\n\ndomain: A domain " - "name. Length - min: 1, max: 200.\n\nipv4: An IPv4 address. Must be a valid IP address.\n\nipv6: An IPv6 address. " - "Must be a valid IP address.\n", + "description": "\nThe type of the indicator. Valid types include:\n\nsha256: A hex-encoded sha256 hash " + " string. Length - min: 64, max: 64.\n\nmd5: A hex-encoded md5 hash string. Length - min 32, max: " + "32.\n\ndomain: A domain name. Length - min: 1, max: 200.\n\nipv4: An IPv4 address. Must be a valid IP " + "address.\n\nipv6: An IPv6 address. Must be a valid IP address.\n", "name": "type", "in": "query", "required": True @@ -221,16 +219,16 @@ "QueryIOCs", "GET", "/indicators/queries/iocs/v1", - "Search the custom IOCs in your customer account. " - "*** Deprecated - Use the new IOC Management endpoint (GET /iocs/queries/indicators/v1). ***", + "Search the custom IOCs in your customer account. *** Deprecated - Use the new IOC Management endpoint " + "(GET /iocs/queries/indicators/v1). ***", "iocs", [ { "type": "string", - "description": "\nThe type of the indicator. Valid types include:\n\nsha256: A hex-encoded sha256 hash string. " - "Length - min: 64, max: 64.\n\nmd5: A hex-encoded md5 hash string. Length - min 32, max: 32.\n\ndomain: A domain " - "name. Length - min: 1, max: 200.\n\nipv4: An IPv4 address. Must be a valid IP address.\n\nipv6: An IPv6 address. " - "Must be a valid IP address.\n", + "description": "\nThe type of the indicator. Valid types include:\n\nsha256: A hex-encoded sha256 hash " + " string. Length - min: 64, max: 64.\n\nmd5: A hex-encoded md5 hash string. Length - min 32, max: " + "32.\n\ndomain: A domain name. Length - min: 1, max: 200.\n\nipv4: An IPv4 address. Must be a valid IP " + "address.\n\nipv6: An IPv6 address. Must be a valid IP address.\n", "name": "types", "in": "query" }, @@ -254,22 +252,22 @@ }, { "type": "string", - "description": "\\ndetect: Find custom IOCs that produce notifications\\n\\nnone: Find custom IOCs the particular " - "indicator has been detected on a host. This is equivalent to turning the indicator off.\n", + "description": "\\ndetect: Find custom IOCs that produce notifications\\n\\nnone: Find custom IOCs the " + "particular indicator has been detected on a host. This is equivalent to turning the indicator off.\n", "name": "policies", "in": "query" }, { "type": "string", - "description": "The source where this indicator originated. This can be used for tracking where this indicator was " - "defined. Limit 200 characters.", + "description": "The source where this indicator originated. This can be used for tracking where this " + "indicator was defined. Limit 200 characters.", "name": "sources", "in": "query" }, { "type": "string", - "description": "The level at which the indicator will be shared. Currently only red share level (not shared) is " - "supported, indicating that the IOC isn't shared with other FH customers.", + "description": "The level at which the indicator will be shared. Currently only red share level (not " + "shared) is supported, indicating that the IOC isn't shared with other FH customers.", "name": "share_levels", "in": "query" }, diff --git a/src/falconpy/_endpoint/_kubernetes_protection.py b/src/falconpy/_endpoint/_kubernetes_protection.py index 1f2348990..372d70f8a 100644 --- a/src/falconpy/_endpoint/_kubernetes_protection.py +++ b/src/falconpy/_endpoint/_kubernetes_protection.py @@ -130,7 +130,7 @@ [ { "type": "boolean", - "description": "(true/false) whether to return registries under assessment or not under assessment. If " + "description": "(true/false) whether to return registries under assessment or not under assessment. If" "not provided all registries are considered", "name": "under_assessment", "in": "query" @@ -538,7 +538,7 @@ { "type": "string", "description": "Filter images using a query in Falcon Query Language (FQL). Supported filters: " - "cid,created_timestamp,detect_timestamp,severity", + "cid,created_timestamp,detect_timestamp,prevented,severity", "name": "filter", "in": "query" } @@ -554,7 +554,7 @@ { "type": "string", "description": "Filter images using a query in Falcon Query Language (FQL). Supported filters: " - "cid,created_timestamp,detect_timestamp,severity", + "cid,created_timestamp,detect_timestamp,prevented,severity", "name": "filter", "in": "query" } @@ -814,7 +814,7 @@ "type": "string", "description": "Search Kubernetes IOMs using a query in Falcon Query Language (FQL). Supported " "filters: cid,cis_id,cluster_id,cluster_name,containers_impacted_count,containers_impacted_ids,detection_type," - "name,namespace,resource_id,resource_name,resource_type,severity", + "name,namespace,prevented,resource_id,resource_name,resource_type,severity", "name": "filter", "in": "query" }, @@ -942,7 +942,7 @@ "type": "string", "description": "Search Kubernetes IOMs using a query in Falcon Query Language (FQL). Supported " "filters: cid,cis_id,cluster_id,cluster_name,containers_impacted_count,containers_impacted_ids,detection_type," - "name,namespace,resource_id,resource_name,resource_type,severity", + "name,namespace,prevented,resource_id,resource_name,resource_type,severity", "name": "filter", "in": "query" }, diff --git a/src/falconpy/_endpoint/_real_time_response.py b/src/falconpy/_endpoint/_real_time_response.py index 0a1c2d664..fb7698794 100644 --- a/src/falconpy/_endpoint/_real_time_response.py +++ b/src/falconpy/_endpoint/_real_time_response.py @@ -92,14 +92,14 @@ }, { "description": "Use this endpoint to run these [real time response " - "commands](https://falcon.crowdstrike.com/support/documentation/11/getting-started-guide#rtr_commands):\n- " - "`cat`\n- `cd`\n- `clear`\n- `cp`\n- `encrypt`\n- `env`\n- `eventlog`\n- `filehash`\n- `get`\n- `getsid`\n- " - "`help`\n- `history`\n- `ipconfig`\n- `kill`\n- `ls`\n- `map`\n- `memdump`\n- `mkdir`\n- `mount`\n- `mv`\n- " - "`netstat`\n- `ps`\n- `reg query`\n- `reg set`\n- `reg delete`\n- `reg load`\n- `reg unload`\n- `restart`\n- " - "`rm`\n- `runscript`\n- `shutdown`\n- `unmap`\n- `update history`\n- `update install`\n- `update list`\n- " - "`update query`\n- `xmemdump`\n- `zip`\n\n**`base_command`** Active-Responder command type we are going to " - "execute, for example: `get` or `cp`. Refer to the RTR documentation for the full list of " - "commands.\n**`batch_id`** Batch ID to execute the command on. Received from `/real-time-" + "commands](https://falcon.crowdstrike.com/documentation/page/b8c1738c/real-time-response-and-network-" + "containment#k893b7c0):\n- `cat`\n- `cd`\n- `clear`\n- `cp`\n- `encrypt`\n- `env`\n- `eventlog`\n- " + "`filehash`\n- `get`\n- `getsid`\n- `help`\n- `history`\n- `ipconfig`\n- `kill`\n- `ls`\n- `map`\n- " + "`memdump`\n- `mkdir`\n- `mount`\n- `mv`\n- `netstat`\n- `ps`\n- `reg query`\n- `reg set`\n- `reg delete`\n- " + "`reg load`\n- `reg unload`\n- `restart`\n- `rm`\n- `runscript`\n- `shutdown`\n- `unmap`\n- `update history`\n-" + " `update install`\n- `update list`\n- `update query`\n- `xmemdump`\n- `zip`\n\n**`base_command`** Active-" + "Responder command type we are going to execute, for example: `get` or `cp`. Refer to the RTR documentation " + "for the full list of commands.\n**`batch_id`** Batch ID to execute the command on. Received from `/real-time-" "response/combined/batch-init-session/v1`.\n**`command_string`** Full command string for the command. For " "example `get some_file.txt`\n**`optional_hosts`** List of a subset of hosts we want to run the command on. " "If this list is supplied, only these hosts will receive the command.", @@ -143,11 +143,11 @@ }, { "description": "Use this endpoint to run these [real time response " - "commands](https://falcon.crowdstrike.com/support/documentation/11/getting-started-guide#rtr_commands):\n- " - "`cat`\n- `cd`\n- `clear`\n- `env`\n- `eventlog`\n- `filehash`\n- `getsid`\n- `help`\n- `history`\n- " - "`ipconfig`\n- `ls`\n- `mount`\n- `netstat`\n- `ps`\n- `reg query`\n\n**`base_command`** read-only command type " - " we are going to execute, for example: `ls` or `cd`. Refer to the RTR documentation for the full list of " - "commands.\n**`batch_id`** Batch ID to execute the command on. Received from `/real-time-" + "commands](https://falcon.crowdstrike.com/documentation/page/b8c1738c/real-time-response-and-network-" + "containment#k893b7c0):\n- `cat`\n- `cd`\n- `clear`\n- `env`\n- `eventlog`\n- `filehash`\n- `getsid`\n- " + "`help`\n- `history`\n- `ipconfig`\n- `ls`\n- `mount`\n- `netstat`\n- `ps`\n- `reg query`\n\n**`base_command`** " + " read-only command type we are going to execute, for example: `ls` or `cd`. Refer to the RTR documentation " + "for the full list of commands.\n**`batch_id`** Batch ID to execute the command on. Received from `/real-time-" "response/combined/batch-init-session/v1`.\n**`command_string`** Full command string for the command. For " "example `cd C:\\some_directory`\n**`optional_hosts`** List of a subset of hosts we want to run the command " "on. If this list is supplied, only these hosts will receive the command.", @@ -345,15 +345,16 @@ [ { "description": "Use this endpoint to run these [real time response " - "commands](https://falcon.crowdstrike.com/support/documentation/11/getting-started-guide#rtr_commands):\n- " - "`cat`\n- `cd`\n- `clear`\n- `cp`\n- `encrypt`\n- `env`\n- `eventlog`\n- `filehash`\n- `get`\n- `getsid`\n- " - "`help`\n- `history`\n- `ipconfig`\n- `kill`\n- `ls`\n- `map`\n- `memdump`\n- `mkdir`\n- `mount`\n- `mv`\n- " - "`netstat`\n- `ps`\n- `reg query`\n- `reg set`\n- `reg delete`\n- `reg load`\n- `reg unload`\n- `restart`\n- " - "`rm`\n- `runscript`\n- `shutdown`\n- `unmap`\n- `update history`\n- `update install`\n- `update list`\n- " - "`update query`\n- `xmemdump`\n- `zip`\n\nRequired values. The rest of the fields are " - "unused.\n**`base_command`** Active-Responder command type we are going to execute, for example: `get` or `cp`. " - " Refer to the RTR documentation for the full list of commands.\n**`command_string`** Full command string for " - "the command. For example `get some_file.txt`\n**`session_id`** RTR session ID to run the command on", + "commands](https://falcon.crowdstrike.com/documentation/page/b8c1738c/real-time-response-and-network-" + "containment#k893b7c0):\n- `cat`\n- `cd`\n- `clear`\n- `cp`\n- `encrypt`\n- `env`\n- `eventlog`\n- " + "`filehash`\n- `get`\n- `getsid`\n- `help`\n- `history`\n- `ipconfig`\n- `kill`\n- `ls`\n- `map`\n- " + "`memdump`\n- `mkdir`\n- `mount`\n- `mv`\n- `netstat`\n- `ps`\n- `reg query`\n- `reg set`\n- `reg delete`\n- " + "`reg load`\n- `reg unload`\n- `restart`\n- `rm`\n- `runscript`\n- `shutdown`\n- `unmap`\n- `update history`\n-" + " `update install`\n- `update list`\n- `update query`\n- `xmemdump`\n- `zip`\n\nRequired values. The rest of " + "the fields are unused.\n**`base_command`** Active-Responder command type we are going to execute, for example: " + " `get` or `cp`. Refer to the RTR documentation for the full list of commands.\n**`command_string`** Full " + "command string for the command. For example `get some_file.txt`\n**`session_id`** RTR session ID to run the " + "command on", "name": "body", "in": "body", "required": True @@ -393,12 +394,13 @@ [ { "description": "Use this endpoint to run these [real time response " - "commands](https://falcon.crowdstrike.com/support/documentation/11/getting-started-guide#rtr_commands):\n- " - "`cat`\n- `cd`\n- `clear`\n- `env`\n- `eventlog`\n- `filehash`\n- `getsid`\n- `help`\n- `history`\n- " - "`ipconfig`\n- `ls`\n- `mount`\n- `netstat`\n- `ps`\n- `reg query`\n\nRequired values. The rest of the fields " - "are unused.\n**`base_command`** read-only command type we are going to execute, for example: `ls` or `cd`. " - "Refer to the RTR documentation for the full list of commands.\n**`command_string`** Full command string for " - "the command. For example `cd C:\\some_directory`\n**`session_id`** RTR session ID to run the command on", + "commands](https://falcon.crowdstrike.com/documentation/page/b8c1738c/real-time-response-and-network-" + "containment#k893b7c0):\n- `cat`\n- `cd`\n- `clear`\n- `env`\n- `eventlog`\n- `filehash`\n- `getsid`\n- " + "`help`\n- `history`\n- `ipconfig`\n- `ls`\n- `mount`\n- `netstat`\n- `ps`\n- `reg query`\n\nRequired values. " + "The rest of the fields are unused.\n**`base_command`** read-only command type we are going to execute, for " + "example: `ls` or `cd`. Refer to the RTR documentation for the full list of commands.\n**`command_string`** " + "Full command string for the command. For example `cd C:\\some_directory`\n**`session_id`** RTR session ID to " + "run the command on", "name": "body", "in": "body", "required": True diff --git a/src/falconpy/_endpoint/_real_time_response_admin.py b/src/falconpy/_endpoint/_real_time_response_admin.py index db9b6558c..1aa0e3a8a 100644 --- a/src/falconpy/_endpoint/_real_time_response_admin.py +++ b/src/falconpy/_endpoint/_real_time_response_admin.py @@ -71,17 +71,17 @@ }, { "description": "Use this endpoint to run these [real time response " - "commands](https://falcon.crowdstrike.com/support/documentation/11/getting-started-guide#rtr_commands):\n- " - "`cat`\n- `cd`\n- `clear`\n- `cp`\n- `encrypt`\n- `env`\n- `eventlog`\n- `filehash`\n- `get`\n- `getsid`\n- " - "`help`\n- `history`\n- `ipconfig`\n- `kill`\n- `ls`\n- `map`\n- `memdump`\n- `mkdir`\n- `mount`\n- `mv`\n- " - "`netstat`\n- `ps`\n- `put`\n- `reg query`\n- `reg set`\n- `reg delete`\n- `reg load`\n- `reg unload`\n- " - "`restart`\n- `rm`\n- `run`\n- `runscript`\n- `shutdown`\n- `unmap`\n- `update history`\n- `update install`\n- " - "`update list`\n- `update query`\n- `xmemdump`\n- `zip`\n\n**`base_command`** Active-Responder command type we " - "are going to execute, for example: `get` or `cp`. Refer to the RTR documentation for the full list of " - "commands.\n**`batch_id`** Batch ID to execute the command on. Received from `/real-time-" - "response/combined/batch-init-session/v1`.\n**`command_string`** Full command string for the command. For " - "example `get some_file.txt`\n**`optional_hosts`** List of a subset of hosts we want to run the command on. " - "If this list is supplied, only these hosts will receive the command.", + "commands](https://falcon.crowdstrike.com/documentation/page/b8c1738c/real-time-response-and-network-" + "containment#k893b7c0):\n- `cat`\n- `cd`\n- `clear`\n- `cp`\n- `encrypt`\n- `env`\n- `eventlog`\n- " + "`filehash`\n- `get`\n- `getsid`\n- `help`\n- `history`\n- `ipconfig`\n- `kill`\n- `ls`\n- `map`\n- " + "`memdump`\n- `mkdir`\n- `mount`\n- `mv`\n- `netstat`\n- `ps`\n- `put`\n- `reg query`\n- `reg set`\n- `reg " + "delete`\n- `reg load`\n- `reg unload`\n- `restart`\n- `rm`\n- `run`\n- `runscript`\n- `shutdown`\n- `unmap`\n-" + " `update history`\n- `update install`\n- `update list`\n- `update query`\n- `xmemdump`\n- " + "`zip`\n\n**`base_command`** Active-Responder command type we are going to execute, for example: `get` or `cp`. " + " Refer to the RTR documentation for the full list of commands.\n**`batch_id`** Batch ID to execute the " + "command on. Received from `/real-time-response/combined/batch-init-session/v1`.\n**`command_string`** Full " + "command string for the command. For example `get some_file.txt`\n**`optional_hosts`** List of a subset of " + "hosts we want to run the command on. If this list is supplied, only these hosts will receive the command.", "name": "body", "in": "body", "required": True @@ -121,15 +121,16 @@ [ { "description": "Use this endpoint to run these [real time response " - "commands](https://falcon.crowdstrike.com/support/documentation/11/getting-started-guide#rtr_commands):\n- " - "`cat`\n- `cd`\n- `clear`\n- `cp`\n- `encrypt`\n- `env`\n- `eventlog`\n- `filehash`\n- `get`\n- `getsid`\n- " - "`help`\n- `history`\n- `ipconfig`\n- `kill`\n- `ls`\n- `map`\n- `memdump`\n- `mkdir`\n- `mount`\n- `mv`\n- " - "`netstat`\n- `ps`\n- `put`\n- `reg query`\n- `reg set`\n- `reg delete`\n- `reg load`\n- `reg unload`\n- " - "`restart`\n- `rm`\n- `run`\n- `runscript`\n- `shutdown`\n- `unmap`\n- `update history`\n- `update install`\n- " - "`update list`\n- `update query`\n- `xmemdump`\n- `zip`\n\nRequired values. The rest of the fields are " - "unused.\n**`base_command`** Active-Responder command type we are going to execute, for example: `get` or `cp`. " - " Refer to the RTR documentation for the full list of commands.\n**`command_string`** Full command string for " - "the command. For example `get some_file.txt`\n**`session_id`** RTR session ID to run the command on", + "commands](https://falcon.crowdstrike.com/documentation/page/b8c1738c/real-time-response-and-network-" + "containment#k893b7c0):\n- `cat`\n- `cd`\n- `clear`\n- `cp`\n- `encrypt`\n- `env`\n- `eventlog`\n- " + "`filehash`\n- `get`\n- `getsid`\n- `help`\n- `history`\n- `ipconfig`\n- `kill`\n- `ls`\n- `map`\n- " + "`memdump`\n- `mkdir`\n- `mount`\n- `mv`\n- `netstat`\n- `ps`\n- `put`\n- `reg query`\n- `reg set`\n- `reg " + "delete`\n- `reg load`\n- `reg unload`\n- `restart`\n- `rm`\n- `run`\n- `runscript`\n- `shutdown`\n- `unmap`\n-" + " `update history`\n- `update install`\n- `update list`\n- `update query`\n- `xmemdump`\n- `zip`\n\nRequired " + "values. The rest of the fields are unused.\n**`base_command`** Active-Responder command type we are going to " + "execute, for example: `get` or `cp`. Refer to the RTR documentation for the full list of " + "commands.\n**`command_string`** Full command string for the command. For example `get " + "some_file.txt`\n**`session_id`** RTR session ID to run the command on", "name": "body", "in": "body", "required": True diff --git a/src/falconpy/_endpoint/_sensor_download.py b/src/falconpy/_endpoint/_sensor_download.py index dabf265c6..6ac86964b 100644 --- a/src/falconpy/_endpoint/_sensor_download.py +++ b/src/falconpy/_endpoint/_sensor_download.py @@ -75,6 +75,44 @@ } ] ], + [ + "GetCombinedSensorInstallersByQueryV2", + "GET", + "/sensors/combined/installers/v2", + "Get sensor installer details by provided query", + "sensor_download", + [ + { + "type": "integer", + "description": "The first item to return, where 0 is the latest item. Use with the limit parameter to " + "manage pagination of results.", + "name": "offset", + "in": "query" + }, + { + "type": "integer", + "description": "The number of items to return in this response (default: 100, max: 500). Use with the " + "offset parameter to manage pagination of results.", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "Sort items using their properties. Common sort options " + "include:\n\n", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "description": "Filter items using a query in Falcon Query Language (FQL). An asterisk wildcard * " + "includes all results.\n\nCommon filter options " + "include:\n", + "name": "filter", + "in": "query" + } + ] + ], [ "DownloadSensorInstallerById", "GET", @@ -91,6 +129,22 @@ } ] ], + [ + "DownloadSensorInstallerByIdV2", + "GET", + "/sensors/entities/download-installer/v2", + "Download sensor installer by SHA256 ID", + "sensor_download", + [ + { + "type": "string", + "description": "SHA256 of the installer to download", + "name": "id", + "in": "query", + "required": True + } + ] + ], [ "GetSensorInstallersEntities", "GET", @@ -111,6 +165,26 @@ } ] ], + [ + "GetSensorInstallersEntitiesV2", + "GET", + "/sensors/entities/installers/v2", + "Get sensor installer details by provided SHA256 IDs", + "sensor_download", + [ + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", + "description": "The IDs of the installers", + "name": "ids", + "in": "query", + "required": True + } + ] + ], [ "GetSensorInstallersCCIDByQuery", "GET", @@ -156,5 +230,43 @@ "in": "query" } ] + ], + [ + "GetSensorInstallersByQueryV2", + "GET", + "/sensors/queries/installers/v2", + "Get sensor installer IDs by provided query", + "sensor_download", + [ + { + "type": "integer", + "description": "The first item to return, where 0 is the latest item. Use with the limit parameter to " + "manage pagination of results.", + "name": "offset", + "in": "query" + }, + { + "type": "integer", + "description": "The number of items to return in this response (default: 100, max: 500). Use with the " + "offset parameter to manage pagination of results.", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "Sort items using their properties. Common sort options " + "include:\n\n", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "description": "Filter items using a query in Falcon Query Language (FQL). An asterisk wildcard * " + "includes all results.\n\nCommon filter options " + "include:\n", + "name": "filter", + "in": "query" + } + ] ] ] diff --git a/src/falconpy/_endpoint/_workflows.py b/src/falconpy/_endpoint/_workflows.py index 19a493460..f164184e7 100644 --- a/src/falconpy/_endpoint/_workflows.py +++ b/src/falconpy/_endpoint/_workflows.py @@ -124,6 +124,13 @@ "name": "id", "in": "query", "required": True + }, + { + "type": "boolean", + "default": True, + "description": "whether or not to sanitize PII from workflow before it's exported", + "name": "sanitize", + "in": "query" } ] ], @@ -179,17 +186,63 @@ ] ], [ - "WorkflowDefinitionsCreate", + "WorkflowExecuteInternal", "POST", - "/workflows/entities/definitions/v1", - "Creates a workflow definition based on the provided model", + "/workflows/entities/execute/internal/v1", + "Executes an on-demand Workflow - internal workflows permitted, the body is JSON used to trigger the " + "execution, the response the execution ID(s)", "workflows", [ { - "type": "boolean", - "default": False, - "description": "When enabled, prevents saving workflow after validating", - "name": "validate_only", + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "CID(s) to execute on. This can be a child if this is a flight control enabled " + "definition. If unset the definition CID is used.", + "name": "execution_cid", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "Definition ID to execute, either a name or an ID can be specified.", + "name": "definition_id", + "in": "query" + }, + { + "type": "string", + "description": "Workflow name to execute, either a name or an ID can be specified.", + "name": "name", + "in": "query" + }, + { + "type": "string", + "description": "Key used to help deduplicate executions, if unset a new UUID is used", + "name": "key", + "in": "query" + }, + { + "type": "integer", + "description": "Used to record the execution depth to help limit execution loops when a workflow " + "triggers another. The maximum depth is 4.", + "name": "depth", + "in": "query" + }, + { + "type": "integer", + "description": "Used to set the batchSize, if unset the default batchSize is used", + "name": "batch_size", + "in": "query" + }, + { + "type": "string", + "description": "Used to record a URL to the source that led to triggering this workflow", + "name": "source_event_url", "in": "query" }, { @@ -349,6 +402,71 @@ } ] ], + [ + "WorkflowMockExecute", + "POST", + "/workflows/entities/mock-executions/v1", + "Executes a workflow definition with mocks", + "workflows", + [ + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "CID(s) to execute on. This can be a child if this is a flight control enabled " + "definition. If unset the definition CID is used.", + "name": "execution_cid", + "in": "query" + }, + { + "type": "string", + "description": "Definition ID to execute, either a name or an ID, or the definition itself in the " + "request body, can be specified.", + "name": "definition_id", + "in": "query" + }, + { + "type": "string", + "description": "Workflow name to execute, either a name or an ID, or the definition itself in the " + "request body, can be specified.", + "name": "name", + "in": "query" + }, + { + "type": "string", + "description": "Key used to help deduplicate executions, if unset a new UUID is used", + "name": "key", + "in": "query" + }, + { + "type": "integer", + "description": "Used to record the execution depth to help limit execution loops when a workflow " + "triggers another. The maximum depth is 4.", + "name": "depth", + "in": "query" + }, + { + "type": "string", + "description": "Used to record a URL to the source that led to triggering this workflow", + "name": "source_event_url", + "in": "query" + }, + { + "type": "boolean", + "default": False, + "description": "When enabled, prevents execution after validating mocks against definition", + "name": "validate_only", + "in": "query" + }, + { + "name": "body", + "in": "body", + "required": True + } + ] + ], [ "WorkflowSystemDefinitionsDeProvision", "POST", diff --git a/src/falconpy/_endpoint/deprecated/_discover.py b/src/falconpy/_endpoint/deprecated/_discover.py index b1b9a98ae..0a9be09eb 100644 --- a/src/falconpy/_endpoint/deprecated/_discover.py +++ b/src/falconpy/_endpoint/deprecated/_discover.py @@ -384,6 +384,54 @@ } ] ], + [ + "query-iot-hostsV2", + "GET", + "/discover/queries/iot-hosts/v2", + "Search for IoT assets in your environment by providing an FQL (Falcon Query Language) filter and paging " + "details. Returns a set of asset IDs which match the filter criteria.", + "discover", + [ + { + "type": "string", + "description": "A pagination token used with the `limit` parameter to manage pagination of results. On " + " your first request, don't provide an `after` token. On subsequent requests, provide the `after` token from " + "the previous response to continue from that place in the results.", + "name": "after", + "in": "query" + }, + { + "maximum": 100, + "minimum": 1, + "type": "integer", + "description": "The number of asset IDs to return in this response (min: 1, max: 100, default: 100). " + "Use with the `after` parameter to manage pagination of results.", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "Sort assets by their properties. A single sort field is allowed. Common sort options " + "include:\n\n", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "description": "Filter assets using an FQL query. Common filter options include:\n\t\t\tAvailable filter fields that support exact match: device_family, device_class, " + "device_type, device_mode, business_criticality, line_of_business, virtual_zone, subnet, purdue_level, vlan, " + "local_ip_addresses, mac_addresses, physical_connections_count, data_providers\n\t\t\tAvailable filter fields " + "that supports wildcard (*): device_family, device_class, device_type, device_mode, business_criticality, " + "line_of_business, virtual_zone, subnet, purdue_level, vlan, local_ip_addresses, mac_addresses, " + "data_providers\n\t\t\tAvailable filter fields that supports range comparisons (>, <, >=, <=): " + "physical_connections_count\n\t\t\tAll filter fields and operations supports negation (!).", + "name": "filter", + "in": "query" + } + ] + ], [ "query-logins", "GET", diff --git a/src/falconpy/_endpoint/deprecated/_real_time_response.py b/src/falconpy/_endpoint/deprecated/_real_time_response.py index 3880fef4d..5b94534f8 100644 --- a/src/falconpy/_endpoint/deprecated/_real_time_response.py +++ b/src/falconpy/_endpoint/deprecated/_real_time_response.py @@ -91,15 +91,16 @@ [ { "description": "Use this endpoint to run these [real time response " - "commands](https://falcon.crowdstrike.com/support/documentation/11/getting-started-guide#rtr_commands):\n- " - "`cat`\n- `cd`\n- `clear`\n- `cp`\n- `encrypt`\n- `env`\n- `eventlog`\n- `filehash`\n- `get`\n- `getsid`\n- " - "`help`\n- `history`\n- `ipconfig`\n- `kill`\n- `ls`\n- `map`\n- `memdump`\n- `mkdir`\n- `mount`\n- `mv`\n- " - "`netstat`\n- `ps`\n- `reg query`\n- `reg set`\n- `reg delete`\n- `reg load`\n- `reg unload`\n- `restart`\n- " - "`rm`\n- `runscript`\n- `shutdown`\n- `unmap`\n- `update history`\n- `update install`\n- `update list`\n- " - "`update query`\n- `xmemdump`\n- `zip`\n\nRequired values. The rest of the fields are " - "unused.\n**`base_command`** Active-Responder command type we are going to execute, for example: `get` or `cp`. " - " Refer to the RTR documentation for the full list of commands.\n**`command_string`** Full command string for " - "the command. For example `get some_file.txt`\n**`session_id`** RTR session ID to run the command on", + "commands](https://falcon.crowdstrike.com/documentation/page/b8c1738c/real-time-response-and-network-" + "containment#k893b7c0):\n- `cat`\n- `cd`\n- `clear`\n- `cp`\n- `encrypt`\n- `env`\n- `eventlog`\n- " + "`filehash`\n- `get`\n- `getsid`\n- `help`\n- `history`\n- `ipconfig`\n- `kill`\n- `ls`\n- `map`\n- " + "`memdump`\n- `mkdir`\n- `mount`\n- `mv`\n- `netstat`\n- `ps`\n- `reg query`\n- `reg set`\n- `reg delete`\n- " + "`reg load`\n- `reg unload`\n- `restart`\n- `rm`\n- `runscript`\n- `shutdown`\n- `unmap`\n- `update history`\n-" + " `update install`\n- `update list`\n- `update query`\n- `xmemdump`\n- `zip`\n\nRequired values. The rest of " + "the fields are unused.\n**`base_command`** Active-Responder command type we are going to execute, for example: " + " `get` or `cp`. Refer to the RTR documentation for the full list of commands.\n**`command_string`** Full " + "command string for the command. For example `get some_file.txt`\n**`session_id`** RTR session ID to run the " + "command on", "name": "body", "in": "body", "required": True @@ -139,12 +140,13 @@ [ { "description": "Use this endpoint to run these [real time response " - "commands](https://falcon.crowdstrike.com/support/documentation/11/getting-started-guide#rtr_commands):\n- " - "`cat`\n- `cd`\n- `clear`\n- `env`\n- `eventlog`\n- `filehash`\n- `getsid`\n- `help`\n- `history`\n- " - "`ipconfig`\n- `ls`\n- `mount`\n- `netstat`\n- `ps`\n- `reg query`\n\nRequired values. The rest of the fields " - "are unused.\n**`base_command`** read-only command type we are going to execute, for example: `ls` or `cd`. " - "Refer to the RTR documentation for the full list of commands.\n**`command_string`** Full command string for " - "the command. For example `cd C:\\some_directory`\n**`session_id`** RTR session ID to run the command on", + "commands](https://falcon.crowdstrike.com/documentation/page/b8c1738c/real-time-response-and-network-" + "containment#k893b7c0):\n- `cat`\n- `cd`\n- `clear`\n- `env`\n- `eventlog`\n- `filehash`\n- `getsid`\n- " + "`help`\n- `history`\n- `ipconfig`\n- `ls`\n- `mount`\n- `netstat`\n- `ps`\n- `reg query`\n\nRequired values. " + "The rest of the fields are unused.\n**`base_command`** read-only command type we are going to execute, for " + "example: `ls` or `cd`. Refer to the RTR documentation for the full list of commands.\n**`command_string`** " + "Full command string for the command. For example `cd C:\\some_directory`\n**`session_id`** RTR session ID to " + "run the command on", "name": "body", "in": "body", "required": True diff --git a/src/falconpy/_endpoint/deprecated/_real_time_response_admin.py b/src/falconpy/_endpoint/deprecated/_real_time_response_admin.py index 8102579c1..4cf4cb73e 100644 --- a/src/falconpy/_endpoint/deprecated/_real_time_response_admin.py +++ b/src/falconpy/_endpoint/deprecated/_real_time_response_admin.py @@ -70,15 +70,16 @@ [ { "description": "Use this endpoint to run these [real time response " - "commands](https://falcon.crowdstrike.com/support/documentation/11/getting-started-guide#rtr_commands):\n- " - "`cat`\n- `cd`\n- `clear`\n- `cp`\n- `encrypt`\n- `env`\n- `eventlog`\n- `filehash`\n- `get`\n- `getsid`\n- " - "`help`\n- `history`\n- `ipconfig`\n- `kill`\n- `ls`\n- `map`\n- `memdump`\n- `mkdir`\n- `mount`\n- `mv`\n- " - "`netstat`\n- `ps`\n- `put`\n- `reg query`\n- `reg set`\n- `reg delete`\n- `reg load`\n- `reg unload`\n- " - "`restart`\n- `rm`\n- `run`\n- `runscript`\n- `shutdown`\n- `unmap`\n- `update history`\n- `update install`\n- " - "`update list`\n- `update query`\n- `xmemdump`\n- `zip`\n\nRequired values. The rest of the fields are " - "unused.\n**`base_command`** Active-Responder command type we are going to execute, for example: `get` or `cp`. " - " Refer to the RTR documentation for the full list of commands.\n**`command_string`** Full command string for " - "the command. For example `get some_file.txt`\n**`session_id`** RTR session ID to run the command on", + "commands](https://falcon.crowdstrike.com/documentation/page/b8c1738c/real-time-response-and-network-" + "containment#k893b7c0):\n- `cat`\n- `cd`\n- `clear`\n- `cp`\n- `encrypt`\n- `env`\n- `eventlog`\n- " + "`filehash`\n- `get`\n- `getsid`\n- `help`\n- `history`\n- `ipconfig`\n- `kill`\n- `ls`\n- `map`\n- " + "`memdump`\n- `mkdir`\n- `mount`\n- `mv`\n- `netstat`\n- `ps`\n- `put`\n- `reg query`\n- `reg set`\n- `reg " + "delete`\n- `reg load`\n- `reg unload`\n- `restart`\n- `rm`\n- `run`\n- `runscript`\n- `shutdown`\n- `unmap`\n-" + " `update history`\n- `update install`\n- `update list`\n- `update query`\n- `xmemdump`\n- `zip`\n\nRequired " + "values. The rest of the fields are unused.\n**`base_command`** Active-Responder command type we are going to " + "execute, for example: `get` or `cp`. Refer to the RTR documentation for the full list of " + "commands.\n**`command_string`** Full command string for the command. For example `get " + "some_file.txt`\n**`session_id`** RTR session ID to run the command on", "name": "body", "in": "body", "required": True diff --git a/src/falconpy/_payload/__init__.py b/src/falconpy/_payload/__init__.py index 04e31a64f..f30d7e906 100644 --- a/src/falconpy/_payload/__init__.py +++ b/src/falconpy/_payload/__init__.py @@ -58,7 +58,12 @@ aws_d4c_registration_payload, gcp_registration_payload ) -from ._cspm_registration import cspm_registration_payload, cspm_policy_payload, cspm_scan_payload +from ._cspm_registration import ( + cspm_registration_payload, + cspm_policy_payload, + cspm_scan_payload, + cspm_service_account_validate_payload + ) from ._device_control_policy import device_policy_payload, default_device_policy_config_payload from ._falconx import falconx_payload from ._filevantage import ( @@ -83,12 +88,16 @@ from ._alerts import update_alerts_payload from ._sample_uploads import extraction_payload from ._ods import scheduled_scan_payload -from ._cloud_snapshots import snapshot_inventory_payload, snapshot_registration_payload +from ._cloud_snapshots import ( + snapshot_registration_payload, + snapshot_launch_payload + ) from ._workflows import ( workflow_deprovision_payload, workflow_template_payload, workflow_definition_payload, - workflow_human_input + workflow_human_input, + workflow_mock_payload ) from ._foundry import foundry_dynamic_search_payload, foundry_execute_search_payload @@ -112,8 +121,9 @@ "recon_export_job_payload", "default_device_policy_config_payload", "registry_payload", "gcp_registration_payload", "filevantage_rule_group_payload", "filevantage_rule_payload", "filevantage_policy_payload", "filevantage_scheduled_exclusion_payload", - "snapshot_inventory_payload", "snapshot_registration_payload", "workflow_deprovision_payload", + "snapshot_registration_payload", "snapshot_launch_payload", "workflow_deprovision_payload", "workflow_template_payload", "foundry_execute_search_payload", "foundry_dynamic_search_payload", "image_policy_payload", "image_exclusions_payload", "image_group_payload", - "workflow_definition_payload", "workflow_human_input" + "workflow_definition_payload", "workflow_human_input", "workflow_mock_payload", + "cspm_service_account_validate_payload" ] diff --git a/src/falconpy/_payload/_cloud_snapshots.py b/src/falconpy/_payload/_cloud_snapshots.py index 697fcc07c..3306d8c2d 100644 --- a/src/falconpy/_payload/_cloud_snapshots.py +++ b/src/falconpy/_payload/_cloud_snapshots.py @@ -76,70 +76,27 @@ def snapshot_registration_payload(passed_keywords: dict) -> dict: return returned_payload -def snapshot_inventory_payload(passed_keywords: dict) -> dict: - """Craft a properly formatted Cloud Snapshot inventory payload. +def snapshot_launch_payload(passed_keywords: dict) -> dict: + """Craft a properly formatted snapshot scan launch payload. { - "job_metadata": { - "cloud_provider": "string", - "instance_id": "string", - "job_end_time": "2023-08-31T02:45:34.131Z", - "job_id": "string", - "job_start_time": "2023-08-31T02:45:34.131Z", - "message": "string", - "scanner_version": "string", - "status": "string" - }, - "results": { - "applications": [ - { - "major_version": "string", - "package_hash": "string", - "package_provider": "string", - "package_source": "string", - "path": "string", - "product": "string", - "software_architecture": "string", - "type": "string", - "vendor": "string" - } - ], - "os_version": "string" - } + "resources": [ + { + "account_id": "string", + "asset_identifier": "string", + "cloud_provider": "string", + "region": "string" + } + ] } """ returned_payload = {} - returned_payload["results"] = {} - # Job metadata - if passed_keywords.get("job_metadata", None): - returned_payload["job_metadata"] = passed_keywords.get("job_metadata", None) - else: - returned_payload["job_metadata"] = {} - # This will only support specifying one job at a time - keys = ["cloud_provider", "instance_id", "job_end_time", "job_id", - "job_start_time", "message", "scanner_version", "status" - ] - for key in keys: - if passed_keywords.get(key, None): - returned_payload["job_metadata"][key] = passed_keywords.get("key", None) - # Job results - if passed_keywords.get("results", None): - returned_payload["results"] = passed_keywords.get("results", None) - else: - if passed_keywords.get("applications", None): - returned_payload["results"]["applications"] = passed_keywords.get("applications", None) - else: - # This will only support specifying one application at a time - returned_payload["results"]["applications"] = [] - keys = ["major_version", "package_hash", "package_provider", "package_source", - "path", "product", "software_architecture", "type", "vendor" - ] - for key in keys: - returned = {} - if passed_keywords.get(key, None): - returned[key] = passed_keywords.get(key, None) - returned_payload["results"]["applications"].append(returned) - if passed_keywords.get("os_version", None): - returned_payload["results"]["os_version"] = passed_keywords.get("os_version", None) + returned_payload["resources"] = [] + keys = ["account_id", "asset_identifier", "cloud_provider", "region"] + item = {} + for key in keys: + if passed_keywords.get(key, None): + item[key] = passed_keywords.get(key, None) + returned_payload["resources"].append(item) return returned_payload diff --git a/src/falconpy/_payload/_cspm_registration.py b/src/falconpy/_payload/_cspm_registration.py index c834f2908..71e14607e 100644 --- a/src/falconpy/_payload/_cspm_registration.py +++ b/src/falconpy/_payload/_cspm_registration.py @@ -170,3 +170,47 @@ def cspm_scan_payload(passed_keywords: dict) -> Dict[str, List[Dict[str, str]]]: returned_payload["resources"].append(item) return returned_payload + + +def cspm_service_account_validate_payload(passed_keywords: dict) -> dict: + """Craft a properly formatted service account validation payload. + + { + "resources": [ + { + "client_email": "string", + "client_id": "string", + "private_key": "string", + "private_key_id": "string", + "project_id": "string", + "service_account_conditions": [ + { + "last_transition": "2024-03-19T22:48:28.987Z", + "message": "string", + "reason": "string", + "status": "string", + "type": "string" + } + ], + "service_account_id": 0 + } + ] + } + """ + returned_payload = { + "resources": [] + } + keys = ["client_email", "client_id", "private_key", "private_key_id", "project_id" + "service_account_conditions", "service_account_id" + ] + if passed_keywords.get("resources", None): + # Providing the resources list of dictionaries overrides other keywords + returned_payload["resources"] = passed_keywords.get("resources", None) + else: + item = {} + for key in keys: + if passed_keywords.get(key, None): + item[key] = passed_keywords.get(key, None) + returned_payload["resources"].append(item) + + return returned_payload diff --git a/src/falconpy/_payload/_workflows.py b/src/falconpy/_payload/_workflows.py index 4d3628921..7dfd6dfd6 100644 --- a/src/falconpy/_payload/_workflows.py +++ b/src/falconpy/_payload/_workflows.py @@ -177,3 +177,23 @@ def workflow_human_input(passed_keywords: dict) -> dict: returned_payload[key] = passed_keywords.get(key, None) return returned_payload + + +def workflow_mock_payload(passed_keywords: dict) -> dict: + """Craft a properly formatted mock execution payload. + + { + "definition" { + Workflow schema + }, + "mocks": "string", + "on_demand_trigger": "string" + } + """ + returned_payload = {} + keys = ["definition", "mocks", "on_demand_trigger"] + for key in keys: + if passed_keywords.get(key, None): + returned_payload[key] = passed_keywords.get(key, None) + + return returned_payload diff --git a/src/falconpy/_util/_functions.py b/src/falconpy/_util/_functions.py index 012276482..3a62ac8fe 100644 --- a/src/falconpy/_util/_functions.py +++ b/src/falconpy/_util/_functions.py @@ -392,6 +392,11 @@ def perform_request(endpoint: str = "", # Force all requests to pass the User-Agent identifier headers["User-Agent"] = _USER_AGENT headers["CrowdStrike-SDK"] = _USER_AGENT + # Clean up query string booleans - Issue #1129 + if api.param_payload: + for param, param_value in api.param_payload.items(): + if isinstance(param_value, bool): + api.param_payload[param] = str(param_value).lower() try: # Log our payloads if debugging is enabled log_api_payloads(api, headers) diff --git a/src/falconpy/_version.py b/src/falconpy/_version.py index cb02b7086..d94a8ccce 100644 --- a/src/falconpy/_version.py +++ b/src/falconpy/_version.py @@ -35,7 +35,7 @@ For more information, please refer to """ -_VERSION = '1.4.1' +_VERSION = '1.4.2' _MAINTAINER = 'Joshua Hiller' _AUTHOR = 'CrowdStrike' _AUTHOR_EMAIL = 'falconpy@crowdstrike.com' diff --git a/src/falconpy/alerts.py b/src/falconpy/alerts.py index 46ad936d7..3ca7e7ad1 100644 --- a/src/falconpy/alerts.py +++ b/src/falconpy/alerts.py @@ -141,8 +141,12 @@ def get_aggregate_alerts_v1(self, body: list = None, **kwargs) -> Dict[str, Unio body=body ) - @force_default(defaults=["body"], default_types=["list"]) - def get_aggregate_alerts_v2(self, body: list = None, **kwargs) -> Dict[str, Union[int, dict]]: + @force_default(defaults=["body", "parameters"], default_types=["list", "dict"]) + def get_aggregate_alerts_v2(self, + body: list = None, + parameters: Optional[Dict[str, List[Union[str, Dict[str, str]]]]] = None, + **kwargs + ) -> Dict[str, Union[int, dict]]: """Retrieve aggregates for Alerts across all CIDs. Keyword arguments: @@ -192,11 +196,13 @@ def get_aggregate_alerts_v2(self, body: list = None, **kwargs) -> Dict[str, Unio String. from -- Integer. include -- Fields to include. String. + include_hidden -- Allows previously hidden alerts to be retrieved. interval -- String. max_doc_count -- Maximum number of documents. Integer. min_doc_count -- Minimum number of documents. Integer. missing -- String. name -- Scan name. String. + parameters - full parameters payload, not required if using other keywords. q -- FQL syntax. String. ranges -- List of dictionaries. size -- Integer. @@ -222,7 +228,8 @@ def get_aggregate_alerts_v2(self, body: list = None, **kwargs) -> Dict[str, Unio calling_object=self, endpoints=Endpoints, operation_id="PostAggregatesAlertsV2", - body=body + body=body, + params=parameters ) # PatchEntitiesAlertsV1 has been **DECOMISSIONED** @@ -375,10 +382,11 @@ def update_alerts_v2(self, body=body ) - @force_default(defaults=["body"], default_types=["dict"]) + @force_default(defaults=["body", "parameters"], default_types=["dict", "dict"]) def update_alerts_v3(self, *args, body: Optional[Dict[str, List[Union[str, Dict[str, str]]]]] = None, + parameters: Optional[Dict[str, List[Union[str, Dict[str, str]]]]] = None, **kwargs ) -> Dict[str, Union[int, dict]]: """Perform actions on alerts identified by detection ID(s) in request. @@ -407,8 +415,10 @@ def update_alerts_v3(self, ] } composite_ids -- ID(s) of the alert to update. String or list of strings. + include_hidden -- Allows previously hidden alerts to be retrieved. new_behavior_processed -- adds a newly processed behavior to 1 or more alert(s). String. Overridden by action_parameters. + parameters - full parameters payload, not required if using other keywords. remove_tag -- remove a tag from 1 or more alert(s). String. Overridden by action_parameters. remove_tags_by_prefix -- remove tags with given prefix from 1 or more alert(s). String. @@ -447,7 +457,8 @@ def update_alerts_v3(self, calling_object=self, endpoints=Endpoints, operation_id="PatchEntitiesAlertsV3", - body=body + body=body, + params=parameters ) @force_default(defaults=["body"], default_types=["dict"]) @@ -488,8 +499,13 @@ def get_alerts_v1(self, *args, body: dict = None, **kwargs) -> Dict[str, Union[i body_required=["ids"] if self.validate_payloads else None ) - @force_default(defaults=["body"], default_types=["dict"]) - def get_alerts_v2(self, *args, body: dict = None, **kwargs) -> Dict[str, Union[int, dict]]: + @force_default(defaults=["body", "parameters"], default_types=["dict", "dict"]) + def get_alerts_v2(self, + *args, + body: Optional[Dict[str, List[Union[str, Dict[str, str]]]]] = None, + parameters: Optional[Dict[str, List[Union[str, Dict[str, str]]]]] = None, + **kwargs + ) -> Dict[str, Union[int, dict]]: """Retrieve all Alerts given their IDs. Keyword arguments: @@ -500,6 +516,8 @@ def get_alerts_v2(self, *args, body: dict = None, **kwargs) -> Dict[str, Union[i ] } composite_ids -- ID(s) of the detections to retrieve. String or list of strings. + include_hidden -- Allows previously hidden alerts to be retrieved. + parameters - full parameters payload, not required if using other keywords. Arguments: When not specified, the first argument to this method is assumed to be 'composite_ids'. All others are ignored. @@ -523,7 +541,8 @@ def get_alerts_v2(self, *args, body: dict = None, **kwargs) -> Dict[str, Union[i operation_id="PostEntitiesAlertsV2", body=body, body_validator={"composite_ids": list} if self.validate_payloads else None, - body_required=["composite_ids"] if self.validate_payloads else None + body_required=["composite_ids"] if self.validate_payloads else None, + params=parameters ) @force_default(defaults=["parameters"], default_types=["dict"]) @@ -572,6 +591,7 @@ def query_alerts_v2(self, parameters: dict = None, **kwargs) -> Dict[str, Union[ For more detail regarding filtering options, please review: https://falcon.crowdstrike.com/documentation/86/detections-monitoring-apis#find-detections + include_hidden -- Allows previously hidden alerts to be retrieved. limit -- The maximum number of detections to return in this response. [Integer, default: 10000; max: 10000] Use with the offset parameter to manage pagination of results. diff --git a/src/falconpy/cloud_snapshots.py b/src/falconpy/cloud_snapshots.py index 4fb9f7c5a..c961728cf 100644 --- a/src/falconpy/cloud_snapshots.py +++ b/src/falconpy/cloud_snapshots.py @@ -36,8 +36,11 @@ For more information, please refer to """ from typing import Dict, Union -from ._util import process_service_request, force_default -from ._payload import snapshot_registration_payload, snapshot_inventory_payload +from ._util import process_service_request, force_default, handle_single_argument +from ._payload import ( + snapshot_registration_payload, + snapshot_launch_payload + ) from ._service_class import ServiceClass from ._endpoint._cloud_snapshots import _cloud_snapshots_endpoints as Endpoints @@ -55,88 +58,91 @@ class CloudSnapshots(ServiceClass): - a valid token provided by the authentication service class (oauth2.py) """ - def get_credentials(self: object) -> Dict[str, Union[int, dict]]: - """Retrieve the registry credentials. + @force_default(defaults=["parameters"], default_types=["dict"]) + def search_scan_jobs(self: object, parameters: dict = None, **kwargs) -> Dict[str, Union[int, dict]]: + """Search for snapshot jobs identified by the provided filter. + + Keyword arguments: + filter -- The filter expression that should be used to limit the results. FQL syntax. + Available sort fields: + account_id region + asset_identifier status + cloud_provider + limit -- The upper-bound on the number of records to retrieve. + Use with the offset parameter to manage pagination of results. + offset -- The offset from where to begin. + Use with the limit parameter to manage pagination of results. + parameters - full parameters payload, not required if using other keywords. + sort -- The property to sort by. FQL syntax (e.g. last_behavior|asc). + Available sort fields: + account_id last_updated_timestamp + asset_identifier region + cloud_provider status + instance_type + + This method only supports keywords for providing arguments. + + Returns: dict object containing API response. HTTP Method: GET Swagger URL - ---- - https://assets.falcon.crowdstrike.com/support/api/swagger.html#/cloud-snapshots/GetCredentialsMixin0 + https://assets.falcon.crowdstrike.com/support/api/swagger.html#/cloud-snapshots/ReadDeploymentsCombined + """ + return process_service_request( + calling_object=self, + endpoints=Endpoints, + operation_id="ReadDeploymentsCombined", + keywords=kwargs, + params=parameters + ) - Keyword arguments - ---- - This method does not accept keyword arguments. + @force_default(defaults=["parameters"], default_types=["dict"]) + def get_scan_jobs(self: object, *args, parameters: dict = None, **kwargs) -> Dict[str, Union[int, dict]]: + """Retrieve snapshot jobs identified by the provided IDs. - Arguments - ---- - This method does not accept arguments. + Keyword arguments: + parameters -- full parameters payload, not required if using other keywords. + ids -- ID(s) of the snapshots to retrieve. String or list of strings. Max: 100 - Returns - ---- - dict - Dictionary object containing API response. + Arguments: When not specified, the first argument to this method is assumed to be 'ids'. + All others are ignored. + + Returns: dict object containing API response. + + HTTP Method: GET + + Swagger URL + https://assets.falcon.crowdstrike.com/support/api/swagger.html#/cloud-snapshots/ReadDeploymentsEntities """ return process_service_request( calling_object=self, endpoints=Endpoints, - operation_id="GetCredentialsMixin0" + operation_id="ReadDeploymentsEntities", + keywords=kwargs, + params=handle_single_argument(args, parameters, "ids") ) @force_default(defaults=["body"], default_types=["dict"]) - def create_inventory(self: object, body: dict = None, **kwargs) -> Dict[str, Union[int, dict]]: - """Create inventory from data received from a snapshot. + def launch_scan_job(self: object, body: dict = None, **kwargs) -> Dict[str, Union[int, dict]]: + """Launch a snapshot scan for a given cloud asset. Keyword arguments: + account_id -- Cloud provider account ID. String. + asset_identifier -- Cloud asset identifier. String. body - full body payload in JSON format, not required if using other keywords. { - "job_metadata": { - "cloud_provider": "string", - "instance_id": "string", - "job_end_time": "2023-08-31T02:45:34.131Z", - "job_id": "string", - "job_start_time": "2023-08-31T02:45:34.131Z", - "message": "string", - "scanner_version": "string", - "status": "string" - }, - "results": { - "applications": [ - { - "major_version": "string", - "package_hash": "string", - "package_provider": "string", - "package_source": "string", - "path": "string", - "product": "string", - "software_architecture": "string", - "type": "string", - "vendor": "string" - } - ], - "os_version": "string" - } + "resources": [ + { + "account_id": "string", + "asset_identifier": "string", + "cloud_provider": "string", + "region": "string" + } + ] } - cloud_provider -- Name of the cloud provider. String. - instance_id -- ID of the instance. String. - job_end_time -- Completion time for the job. UTC date string. - job_id -- ID of the job. String. - job_start_time -- Start time for the job. UTC date string. - message -- Message received upon job completion. String. - scanner_version -- Version identifier for the scanner used. String. - status -- Job completion status. String. - results -- Full results payload. Dictionary. Overrides values below. - os_version -- Operating system version. String. - applications -- Complete application list. List of dictionaries. Overrides values below. - major_version -- Application major version. String. - package_hash -- Hash for the package. String. - package_provider -- Package provider. String. - path -- File path for the application. String. - product - Product name for the application. String. - software_architecture -- Running architecture for the application. String. - type -- Type of application. String. - vendor -- Application vendor. String. - job_metadata -- Complete job metadata. Dictionary. + cloud_provider -- Cloud provider ID. String. + region -- Cloud provider region ID. String. This method only supports keywords for providing arguments. @@ -145,19 +151,73 @@ def create_inventory(self: object, body: dict = None, **kwargs) -> Dict[str, Uni HTTP Method: POST Swagger URL - https://assets.falcon.crowdstrike.com/support/api/swagger.html#/cloud-snapshots/CreateInventory + https://assets.falcon.crowdstrike.com/support/api/swagger.html#/cloud-snapshots/CreateDeploymentEntity """ if not body: - body = snapshot_inventory_payload(passed_keywords=kwargs) + body = snapshot_launch_payload(passed_keywords=kwargs) return process_service_request( calling_object=self, endpoints=Endpoints, - operation_id="CreateInventory", + operation_id="CreateDeploymentEntity", keywords=kwargs, body=body ) + @force_default(defaults=["parameters"], default_types=["dict"]) + def get_scan_reports(self: object, *args, parameters: dict = None, **kwargs) -> Dict[str, Union[int, dict]]: + """Retrieve the scan report for an instance. + + Keyword arguments: + parameters -- full parameters payload, not required if using other keywords. + ids -- The instance identifiers to fetch reports for. String or list of strings. Max: 100 + + Arguments: When not specified, the first argument to this method is assumed to be 'ids'. + All others are ignored. + + Returns: dict object containing API response. + + HTTP Method: GET + + Swagger URL + https://assets.falcon.crowdstrike.com/support/api/swagger.html#/cloud-snapshots/GetScanReport + """ + return process_service_request( + calling_object=self, + endpoints=Endpoints, + operation_id="GetScanReport", + keywords=kwargs, + params=handle_single_argument(args, parameters, "ids") + ) + + def get_credentials(self: object) -> Dict[str, Union[int, dict]]: + """Retrieve the registry credentials. + + HTTP Method: GET + + Swagger URL + ---- + https://assets.falcon.crowdstrike.com/support/api/swagger.html#/cloud-snapshots/GetCredentialsMixin0 + + Keyword arguments + ---- + This method does not accept keyword arguments. + + Arguments + ---- + This method does not accept arguments. + + Returns + ---- + dict + Dictionary object containing API response. + """ + return process_service_request( + calling_object=self, + endpoints=Endpoints, + operation_id="GetCredentialsMixin0" + ) + @force_default(defaults=["body"], default_types=["dict"]) def register_account(self: object, body: dict = None, **kwargs) -> Dict[str, Union[int, dict]]: """Create inventory from data received from a snapshot. @@ -214,6 +274,9 @@ def register_account(self: object, body: dict = None, **kwargs) -> Dict[str, Uni # This method name aligns to the operation ID in the API but # does not conform to snake_case / PEP8 and is defined here # for backwards compatibility / ease of use purposes + ReadDeploymentsCombined = search_scan_jobs + ReadDeploymentsEntities = get_scan_jobs + CreateDeploymentEntity = launch_scan_job + GetScanReport = get_scan_reports GetCredentialsMixin0 = get_credentials - CreateInventory = create_inventory RegisterCspmSnapshotAccount = register_account diff --git a/src/falconpy/container_detections.py b/src/falconpy/container_detections.py index 720e951bc..f48b70441 100644 --- a/src/falconpy/container_detections.py +++ b/src/falconpy/container_detections.py @@ -196,6 +196,51 @@ def read_combined_detections(self: object, parameters: dict = None, **kwargs) -> params=parameters ) + @force_default(defaults=["parameters"], default_types=["dict"]) + def search_runtime_detections(self: object, parameters: dict = None, **kwargs) -> Dict[str, Union[int, dict]]: + """Retrieve image assessment detections identified by the provided filter criteria. + + Keyword arguments: + filter -- Filter Container Runtime Detections using a query in Falcon Query Language (FQL). String. + Supported filters: + action_taken file_name + aid file_path + cid host_id + cloud host_type + cluster_name image_id + command_line name + computer_name namespace + container_id pod_name + detect_timestamp severity + detection_description tactic + detection_id + + limit -- The upper-bound on the number of records to retrieve. Integer. + offset -- The offset from where to begin. Integer. + sort -- The fields to sort the records on. String. + Supported columns: + containers_impacted detection_type + detection_name images_impacted + detection_severity last_detected + parameters -- Full parameters payload dictionary. Not required if using other keywords. + + This method only supports keywords for providing arguments. + + Returns: dict object containing API response. + + HTTP Method: GET + + Swagger URL + https://assets.falcon.crowdstrike.com/support/api/swagger.html#/container-detections/GetRuntimeDetectionsCombinedV2 + """ + return process_service_request( + calling_object=self, + endpoints=Endpoints, + operation_id="GetRuntimeDetectionsCombinedV2", + keywords=kwargs, + params=parameters + ) + @force_default(defaults=["parameters"], default_types=["dict"]) def read_detections(self: object, parameters: dict = None, **kwargs) -> Dict[str, Union[int, dict]]: """Retrieve image assessment detection entities identified by the provided filter criteria. @@ -262,5 +307,6 @@ def search_detections(self: object, parameters: dict = None, **kwargs) -> Dict[s ReadDetectionsCountByType = read_detections_count_by_type ReadDetectionsCount = read_detections_count ReadCombinedDetections = read_combined_detections + GetRuntimeDetectionsCombinedV2 = search_runtime_detections ReadDetections = read_detections SearchDetections = search_detections diff --git a/src/falconpy/container_images.py b/src/falconpy/container_images.py index 9b68e71f5..0043d085a 100644 --- a/src/falconpy/container_images.py +++ b/src/falconpy/container_images.py @@ -308,7 +308,7 @@ def read_combined_export(self: object, parameters: dict = None, **kwargs) -> Dic first_seen repository highest_detection_severity tag highest_vulnerability_severity vulnerabilities - image_digest + image_digest highest_cps_current_rating parameters -- Full parameters payload dictionary. Not required if using other keywords. This method only supports keywords for providing arguments. diff --git a/src/falconpy/cspm_registration.py b/src/falconpy/cspm_registration.py index 5b5c4e7aa..c98179f0e 100644 --- a/src/falconpy/cspm_registration.py +++ b/src/falconpy/cspm_registration.py @@ -42,7 +42,9 @@ cspm_registration_payload, cspm_policy_payload, cspm_scan_payload, - gcp_registration_payload + gcp_registration_payload, + generic_payload_list, + cspm_service_account_validate_payload ) from ._service_class import ServiceClass from ._endpoint._cspm_registration import _cspm_registration_endpoints as Endpoints @@ -625,6 +627,32 @@ def create_azure_management_group(self: object, body: dict = None, **kwargs) -> body=body ) + @force_default(defaults=["parameters"], default_types=["dict"]) + def delete_azure_management_group(self: object, *args, parameters: dict = None, **kwargs) -> Dict[str, Union[int, dict]]: + """Delete an existing Azure Managment Group by specifying their IDs. + + Keyword arguments: + tenant_ids -- AWS Organization IDs to be removed. String or list of strings. + parameters -- full parameters payload, not required if tenant_ids is provided as a keyword. + + Arguments: When not specified, the first argument to this method is assumed to be + 'tenant_ids'. All others are ignored. + + Returns: dict object containing API response. + + HTTP Method: DELETE + + Swagger URL + https://assets.falcon.crowdstrike.com/support/api/swagger.html#/cspm-registration/DeleteCSPMAzureManagementGroup + """ + return process_service_request( + calling_object=self, + endpoints=Endpoints, + operation_id="DeleteCSPMAzureManagementGroup", + keywords=kwargs, + params=handle_single_argument(args, parameters, "tenant_ids") + ) + @force_default(defaults=["parameters"], default_types=["dict"]) def get_azure_user_scripts_attachment(self: object, *args, @@ -853,6 +881,98 @@ def connect_gcp_account(self: object, body: dict = None, **kwargs) -> Dict[str, body=body ) + @force_default(defaults=["body"], default_types=["dict"]) + def validate_gcp_account(self: object, *args, body: dict = None, **kwargs) -> Dict[str, Union[int, dict]]: + """Run a synchronous health check. + + Keyword arguments: + body -- full body payload, not required if using other keywords. + { + "resources": [ + "string" + ] + } + resources -- GCP Account IDs to validate. String or list of strings. + + Arguments: When not specified, the first argument to this method is assumed to be 'resources'. + All others are ignored. + + Returns: dict object containing API response. + + HTTP Method: POST + + Swagger URL + https://assets.falcon.crowdstrike.com/support/api/swagger.html#/cspm-registration/GetCSPMGCPValidateAccountsExt + """ + if not body: + body = generic_payload_list(submitted_keywords=kwargs, + submitted_arguments=args, + payload_value="resources" + ) + + return process_service_request( + calling_object=self, + endpoints=Endpoints, + operation_id="GetCSPMGCPValidateAccountsExt", + body=body + ) + + @force_default(defaults=["body"], default_types=["dict"]) + def validate_gcp_service_account(self: object, body: dict = None, **kwargs) -> Dict[str, Union[int, dict]]: + """Validate credentials for a GCP service account. + + Keyword arguments: + body -- full body payload, not required if using other keywords. + { + "resources": [ + { + "client_email": "string", + "client_id": "string", + "private_key": "string", + "private_key_id": "string", + "project_id": "string", + "service_account_conditions": [ + { + "last_transition": "2024-03-19T22:48:28.987Z", + "message": "string", + "reason": "string", + "status": "string", + "type": "string" + } + ], + "service_account_id": 0 + } + ] + } + client_email -- Client email associated with the service account. String. + client_id -- GCP Client ID. String. + private_key -- GCP private key. String. + private_key_id -- GCP private key ID. String. + project_id -- GCP project ID. String. + resources -- List of GCP service accounts to validate. List of dictionaries. + Overrides other keywords except for body. + service_account_conditions -- GCP service account conditions. List of dictionaries. + service_account_id -- GCP service account ID. Integer. + + This method only supports keywords for providing arguments. + + Returns: dict object containing API response. + + HTTP Method: POST + + Swagger URL + https://assets.falcon.crowdstrike.com/support/api/swagger.html#/cspm-registration/ValidateCSPMGCPServiceAccountExt + """ + if not body: + body = cspm_service_account_validate_payload(passed_keywords=kwargs) + + return process_service_request( + calling_object=self, + endpoints=Endpoints, + operation_id="ValidateCSPMGCPServiceAccountExt", + body=body + ) + @force_default(defaults=["parameters"], default_types=["dict"]) def get_gcp_service_account(self: object, *args, @@ -1448,6 +1568,7 @@ def update_scan_schedule(self: object, body: dict = None, **kwargs) -> Dict[str, GetCSPMAzureUserScriptsAttachment = get_azure_user_scripts_attachment AzureDownloadCertificate = azure_download_certificate GetCSPMAzureManagementGroup = get_azure_management_group + DeleteCSPMAzureManagementGroup = delete_azure_management_group CreateCSPMAzureManagementGroup = create_azure_management_group GetCSPMCGPAccount = get_gcp_account GetCSPMGCPAccount = get_gcp_account # Typo fix @@ -1455,6 +1576,8 @@ def update_scan_schedule(self: object, body: dict = None, **kwargs) -> Dict[str, DeleteCSPMGCPAccount = delete_gcp_account UpdateCSPMGCPAccount = update_gcp_account ConnectCSPMGCPAccount = connect_gcp_account + GetCSPMGCPValidateAccountsExt = validate_gcp_account + ValidateCSPMGCPServiceAccountExt = validate_gcp_service_account GetCSPMGCPServiceAccountsExt = get_gcp_service_account GetCSPMGCPUserScriptsAttachment = get_gcp_user_scripts_attachment GetBehaviorDetections = get_behavior_detections diff --git a/src/falconpy/discover.py b/src/falconpy/discover.py index 7631e32f3..d5fcfad58 100644 --- a/src/falconpy/discover.py +++ b/src/falconpy/discover.py @@ -470,3 +470,74 @@ def query_iot_hosts(self: object, parameters: dict = None, **kwargs) -> dict: keywords=kwargs, params=parameters ) + + @force_default(defaults=["parameters"], default_types=["dict"]) + def query_iot_hosts_v2(self: object, parameters: dict = None, **kwargs) -> dict: + """Search for IoT assets in your environment. + + Supports providing a FQL (Falcon Query Language) filter and paging details. + Returns a set of asset IDs which match the filter criteria. + + Keyword arguments: + filter -- The filter expression that should be used to limit the results. FQL syntax. + Common filter options include: + entity_type:'managed' + product_type_desc:'Workstation' + platform_name:'Windows' + last_seen_timestamp:>'now-7d' + Available Filters: + agent_version last_seen_timestamp + aid local_ip_addresses + bios_manufacturer local_ips_count + bios_version mac_addresses + business_criticality machine_domain + cid network_id + city network_interfaces + claroty_id number_of_disk_drives + confidence os_is_eol + country os_version + current_local_ip ou + data_providers physical_core_count + data_providers_count platform_name + device_class processor_package_count + device_family product_type_desc + device_type protocols + discoverer_count purdue_level + discoverer_product_type_descs reduced_functionality_mode + entity_type site_name + external_ip subnet + first_seen_timestamp system_manufacturer + groups system_product_name + hostname system_serial_number + ics_id tags + id virtual_zone + internet_exposure vlan + kernel_version + limit -- The number of asset IDs to return in this response. (Max: 100, default: 100) + Use with the offset parameter to manage pagination of results. + offset -- An offset used with the limit parameter to manage pagination of results. + On your first request, don’t provide an offset. On subsequent requests, + provide the offset from the previous response to continue from that place + in the results. + parameters - full parameters payload, not required if using other keywords. + sort -- Sort assets by their properties. A single sort field is allowed. + Common sort options include: + hostname|asc + product_type_desc|desc + + This method only supports keywords for providing arguments. + + Returns: dict object containing API response. + + HTTP Method: GET + + Swagger URL + https://assets.falcon.crowdstrike.com/support/api/swagger.html#/discover-iot/query-iot-hostsV2 + """ + return process_service_request( + calling_object=self, + endpoints=Endpoints, + operation_id="query_iot_hostsV2", + keywords=kwargs, + params=parameters + ) diff --git a/src/falconpy/falcon_complete_dashboard.py b/src/falconpy/falcon_complete_dashboard.py index 5246bf812..216c90132 100644 --- a/src/falconpy/falcon_complete_dashboard.py +++ b/src/falconpy/falcon_complete_dashboard.py @@ -898,6 +898,92 @@ def aggregate_sensor_update_policy(self: object, body: list = None, **kwargs) -> body=body ) + @force_default(defaults=["body"], default_types=["list"]) + def aggregate_support_issues(self: object, body: list = None, **kwargs) -> Dict[str, Union[int, dict]]: + """Retrieve aggregate device count values based on the matched filter. + + Keyword arguments: + body -- full body payload, not required when using other keywords. + List of dictionaries. + [ + { + "date_ranges": [ + { + "from": "string", + "to": "string" + } + ], + "exclude": "string", + "field": "string", + "filter": "string", + "from": 0, + "include": "string", + "interval": "string", + "max_doc_count": 0, + "min_doc_count": 0, + "missing": "string", + "name": "string", + "q": "string", + "ranges": [ + { + "From": 0, + "To": 0 + } + ], + "size": 0, + "sort": "string", + "sub_aggregates": [ + null + ], + "time_zone": "string", + "type": "string" + } + ] + date_ranges -- If peforming a date range query specify the from and to date ranges. + These can be in common date formats like 2019-07-18 or now. + List of dictionaries. + exclude -- Fields to exclude. String. + field -- Term you want to aggregate on. If doing a date_range query, + this is the date field you want to apply the date ranges to. String. + filter -- Optional filter criteria in the form of an FQL query. + For more information about FQL queries, see our FQL documentation in Falcon. + String. + from -- Integer. + include -- Fields to include. String. + interval -- String. + max_doc_count -- Maximum number of documents. Integer. + min_doc_count -- Minimum number of documents. Integer. + missing -- String. + name -- Scan name. String. + q -- FQL syntax. String. + ranges -- List of dictionaries. + size -- Integer. + sort -- FQL syntax. String. + sub_aggregates -- List of strings. + time_zone -- String. + type -- String. + + This method only supports keywords for providing arguments. + + This method does not support body payload validation. + + Returns: dict object containing API response. + + HTTP Method: POST + + Swagger URL + https://assets.falcon.crowdstrike.com/support/api/swagger.html#/Falcon%20Complete%20Dashboard/AggregateSupportIssues + """ + if not body: + body = [aggregate_payload(submitted_keywords=kwargs)] + + return process_service_request( + calling_object=self, + endpoints=Endpoints, + operation_id="AggregateSupportIssues", + body=body + ) + @force_default(defaults=["body"], default_types=["list"]) def aggregate_total_device_counts(self: object, body: list = None, **kwargs) -> Dict[str, Union[int, dict]]: """Retrieve aggregate device count values based on the matched filter. @@ -1245,6 +1331,7 @@ def query_remediations_filter(self: object, parameters: dict = None, **kwargs) - AggregatePreventionPolicy = aggregate_prevention_policy AggregateRemediations = aggregate_remediations AggregateSensorUpdatePolicy = aggregate_sensor_update_policy + AggregateSupportIssues = aggregate_support_issues AggregateTotalDeviceCounts = aggregate_total_device_counts QueryAlertIdsByFilter = query_alert_ids_by_filter QueryAllowListFilter = query_allow_list_filter diff --git a/src/falconpy/filevantage.py b/src/falconpy/filevantage.py index cc9f37c57..5de3a0219 100644 --- a/src/falconpy/filevantage.py +++ b/src/falconpy/filevantage.py @@ -636,6 +636,7 @@ def create_rule(self: object, body: dict = None, **kwargs) -> Dict[str, Union[in macOS is not supported at this time. watch_create_key_changes -- Windows registry key and value monitoring. Boolean. watch_delete_key_changes -- Windows registry key and value monitoring. Boolean. + watch_permissions_key_changes -- Windows registry key permissions monitoring. Boolean. watch_rename_key_changes -- Windows registry key and value monitoring. Boolean. watch_set_value_changes -- Windows registry key and value monitoring. Boolean. watch_delete_value_changes -- Windows registry key and value monitoring. Boolean. diff --git a/src/falconpy/foundry_logscale.py b/src/falconpy/foundry_logscale.py index 446510bff..5869cd8cc 100644 --- a/src/falconpy/foundry_logscale.py +++ b/src/falconpy/foundry_logscale.py @@ -88,7 +88,7 @@ def ingest_data(self: object, parameters: dict = None, **kwargs ) -> dict: - """Ingest data into the application repository. + """Ingest data into the application repository synchronously. Keyword arguments: data_file -- Content of the uploaded archive in binary format. @@ -110,6 +110,11 @@ def ingest_data(self: object, # Try to find the binary object they provided us if not data_file: data_file = kwargs.get("file", None) + data_keys = ["tag", "tag_source", "test_data"] + form_data = {} + for key in data_keys: + if kwargs.get(key, None): + form_data[key] = kwargs.get(key) # Create a multipart form payload for our upload file file_tuple = [("file", ("data-upload", data_file, "application/json"))] @@ -117,7 +122,56 @@ def ingest_data(self: object, calling_object=self, endpoints=Endpoints, operation_id="IngestDataV1", + body=body, + data=form_data, + files=file_tuple, + keywords=kwargs, + params=parameters + ) + + @force_default(defaults=["parameters", "body"], default_types=["dict", "dict"]) + def ingest_data_async(self: object, + data_file: dict = None, + body: dict = None, + parameters: dict = None, + **kwargs + ) -> dict: + """Ingest data into the application repository asynchronously. + + Keyword arguments: + data_file -- Content of the uploaded archive in binary format. + 'file' is also accepted as this parameter. + parameters -- full parameters payload, not required if using other keywords. + tag -- Custom tag for ingested data in the form 'tag:value'. String. + tag_source -- Tag the data with the specified source. String. + test_data -- Tag the data with 'test-ingest'. Defaults to False. Boolean. + + This method only supports keywords for providing arguments. + + Returns: dict object containing API response. + + HTTP Method: POST + + Swagger URL + https://assets.falcon.crowdstrike.com/support/api/swagger.html#/foundry-logscale/IngestDataAsyncV1 + """ + # Try to find the binary object they provided us + if not data_file: + data_file = kwargs.get("file", None) + data_keys = ["tag", "tag_source", "test_data"] + form_data = {} + for key in data_keys: + if kwargs.get(key, None): + form_data[key] = kwargs.get(key) + + # Create a multipart form payload for our upload file + file_tuple = [("file", ("data-upload", data_file, "application/json"))] + return process_service_request( + calling_object=self, + endpoints=Endpoints, + operation_id="IngestDataAsyncV1", body=body, # Not sure we need to provide a body + data=form_data, files=file_tuple, keywords=kwargs, params=parameters @@ -144,6 +198,9 @@ def execute_dynamic(self: object, end -- Ending position. String. include_schema_generation -- Include generated schemas in the response. Boolean. incude_test_data -- Include test data when executing searches. Boolean. + infer_json_types -- Whether to try to infer data types in json event response + instead of returning map[string]string. Boolean. + match_response_schema -- Whether to validate search results against their schema. Boolean. metadata -- Include metadata in the response. Boolean. mode -- Mode to execute the query under (async or sync). String. repo_or_view -- Name of the repo or view to perform the search. String. @@ -181,6 +238,9 @@ def get_search_results(self: object, parameters: dict = None, **kwargs) -> Dict[ job_id -- Job ID for a previously executed asynchronous query. String. limit -- The maximum number of records to return in this response. Integer. Use with the offset parameter to manage pagination of results. + infer_json_types -- Whether to try to infer data types in json event response + instead of returning map[string]string. Boolean. + match_response_schema -- Whether to validate search results against their schema. Boolean. metadata -- Flag indicating if metadata should be included in the results. Boolean. offset -- The offset to start retrieving records from. String. Use with the limit parameter to manage pagination of results. @@ -256,6 +316,9 @@ def execute(self: object, end -- Ending position. String. id -- Saved search ID. String. include_test_data -- Include test data when executing searches. Boolean. + infer_json_types -- Whether to try to infer data types in json event response + instead of returning map[string]string. Boolean. + match_response_schema -- Whether to validate search results against their schema. Boolean. metadata -- Include metadata in the response. Boolean. name -- Saved search name. String. search_parameters -- Search specific parameters. Dictionary. @@ -318,6 +381,8 @@ def download_results(self: object, parameters: dict = None, **kwargs) -> Dict[st Keyword arguments: job_id -- Job ID for a previously executed asynchronous query. String. + infer_json_types -- Whether to try to infer data types in json event response + instead of returning map[string]string. Boolean. parameters - full parameters payload, not required if using other keywords. result_format -- Result file format. Allowed values: 'json' or 'csv'. String. @@ -370,6 +435,7 @@ def list_views(self: object, *args, parameters: dict = None, **kwargs) -> Dict[s ListReposV1 = list_repos ListViewV1 = list_views IngestDataV1 = ingest_data + IngestDataAsyncV1 = ingest_data_async CreateSavedSearchesDynamicExecuteV1 = execute_dynamic GetSavedSearchesExecuteV1 = get_search_results CreateSavedSearchesExecuteV1 = execute diff --git a/src/falconpy/intel.py b/src/falconpy/intel.py index 3d9141516..e29021e11 100644 --- a/src/falconpy/intel.py +++ b/src/falconpy/intel.py @@ -337,6 +337,37 @@ def mitre_attacks(self: object, *args, body: dict = None, **kwargs) -> Dict[str, body_required=["ids"] if self.validate_payloads else None ) + @force_default(defaults=["parameters"], default_types=["dict"]) + def get_malware_entities(self: object, + *args, + parameters: dict = None, + **kwargs) -> Union[Dict[str, Union[int, dict]], bytes]: + """Get malware entities for specified ids. + + Keyword arguments: + ids -- Malware family entities to retrieve. String or list of strings. + Malware family names should be in lower case with spaces, dots and + slashes replaced with dashes. + parameters - full parameters payload, not required if using other keywords. + + Arguments: When not specified, the first argument to this method is assumed to be 'ids'. + All others are ignored. + + Returns: dict object containing API response. + + HTTP Method: GET + + Swagger URL + https://assets.falcon.crowdstrike.com/support/api/swagger.html#/intel/GetMalwareEntities + """ + return process_service_request( + calling_object=self, + endpoints=Endpoints, + operation_id="GetMalwareEntities", + keywords=kwargs, + params=handle_single_argument(args, parameters, "ids") + ) + @force_default(defaults=["parameters"], default_types=["dict"]) def get_report_pdf(self: object, *args, parameters: dict = None, **kwargs) -> object: """Return a Report PDF attachment. @@ -610,6 +641,37 @@ def query_mitre_attacks(self: object, *args, parameters: dict = None, **kwargs) params=handle_single_argument(args, parameters, "ids") ) + @force_default(defaults=["parameters"], default_types=["dict"]) + def query_mitre_attacks_for_malware(self: object, + *args, + parameters: dict = None, + **kwargs) -> Union[Dict[str, Union[int, dict]], bytes]: + """Get MITRE tactics and techniques for the given malware. + + Keyword arguments: + ids -- Malware family entities to retrieve. String or list of strings. + Malware family names should be in lower case with spaces, dots and + slashes replaced with dashes. + parameters - full parameters payload, not required if using other keywords. + + Arguments: When not specified, the first argument to this method is assumed to be 'ids'. + All others are ignored. + + Returns: dict object containing API response. + + HTTP Method: GET + + Swagger URL + https://assets.falcon.crowdstrike.com/support/api/swagger.html#/intel/QueryMitreAttacksForMalware + """ + return process_service_request( + calling_object=self, + endpoints=Endpoints, + operation_id="QueryMitreAttacksForMalware", + keywords=kwargs, + params=handle_single_argument(args, parameters, "ids") + ) + @force_default(defaults=["parameters"], default_types=["dict"]) def query_report_ids(self: object, parameters: dict = None, **kwargs) -> Dict[str, Union[int, dict]]: """Get report IDs that match provided FQL filters. @@ -698,6 +760,35 @@ def query_rule_ids(self: object, parameters: dict = None, **kwargs) -> Dict[str, params=parameters ) + @force_default(defaults=["parameters"], default_types=["dict"]) + def query_malware(self: object, parameters: dict = None, **kwargs) -> Dict[str, Union[int, dict]]: + """Get malware family names that match provided FQL filters. + + Keyword arguments: + filter -- The filter expression that should be used to limit the results. FQL syntax. + limit -- The maximum number of actors to return. [integer, 1-5000] + offset -- The integer offset to start retrieving records from. + parameters - full parameters payload, not required if using other keywords. + q -- Perform a generic substring search across all fields. + sort -- The property to sort by. FQL syntax (e.g. created_date|asc). + + This method only supports keywords for providing arguments. + + Returns: dict object containing API response. + + HTTP Method: GET + + Swagger URL + https://assets.falcon.crowdstrike.com/support/api/swagger.html#/intel/QueryMalware + """ + return process_service_request( + calling_object=self, + endpoints=Endpoints, + operation_id="QueryMalware", + keywords=kwargs, + params=parameters + ) + @force_default(defaults=["body"], default_types=["dict"]) def get_vulnerabilities(self: object, *args, body: dict = None, **kwargs) -> dict: """Retrieve specific vulnerabilities using their indicator IDs. @@ -778,13 +869,16 @@ def query_vulnerabilities(self: object, parameters: dict = None, **kwargs) -> di GetIntelIndicatorEntities = get_indicator_entities GetMitreReport = get_mitre_report PostMitreAttacks = mitre_attacks + GetMalwareEntities = get_malware_entities GetIntelReportPDF = get_report_pdf GetIntelReportEntities = get_report_entities GetIntelRuleFile = get_rule_file GetLatestIntelRuleFile = get_latest_rule_file GetIntelRuleEntities = get_rule_entities QueryMitreAttacks = query_mitre_attacks + QueryMitreAttacksForMalware = query_mitre_attacks_for_malware QueryIntelActorIds = query_actor_ids + QueryMalware = query_malware QueryIntelIndicatorIds = query_indicator_ids QueryIntelReportIds = query_report_ids QueryIntelRuleIds = query_rule_ids diff --git a/src/falconpy/kubernetes_protection.py b/src/falconpy/kubernetes_protection.py index d8df822ee..2a975cb4c 100644 --- a/src/falconpy/kubernetes_protection.py +++ b/src/falconpy/kubernetes_protection.py @@ -942,7 +942,7 @@ def read_iom_count_by_date_range(self: object, *args, parameters: dict = None, * Keyword arguments: filter -- Filter images using a query in Falcon Query Language (FQL). String. - Supported filters: cid, created_timestamp, detect_timestamp, severity + Supported filters: cid, created_timestamp, detect_timestamp, prevented, severity parameters -- Full parameters payload dictionary. Not required if using other keywords. Arguments: When not specified, the first argument to this method is assumed to be 'filter'. @@ -969,7 +969,7 @@ def read_iom_count(self: object, *args, parameters: dict = None, **kwargs) -> Di Keyword arguments: filter -- Filter images using a query in Falcon Query Language (FQL). String. - Supported filters: cid, created_timestamp, detect_timestamp, severity + Supported filters: cid, created_timestamp, detect_timestamp, prevented, severity parameters -- Full parameters payload dictionary. Not required if using other keywords. Arguments: When not specified, the first argument to this method is assumed to be 'filter'. @@ -1368,7 +1368,7 @@ def search_and_read_ioms(self: object, parameters: dict = None, **kwargs) -> Dic cluster_name resource_name containers_impacted_count resource_type containers_impacted_ids severity - detection_type + detection_type prevented limit -- The upper-bound on the number of records to retrieve. Integer. offset -- The offset from where to begin. Integer. sort -- The fields to sort the records on. String. @@ -1513,7 +1513,7 @@ def search_ioms(self: object, parameters: dict = None, **kwargs) -> Dict[str, Un cluster_name resource_name containers_impacted_count resource_type containers_impacted_ids severity - detection_type + detection_type prevented limit -- The upper-bound on the number of records to retrieve. Integer. offset -- The offset from where to begin. Integer. sort -- The fields to sort the records on. String. diff --git a/src/falconpy/oauth2.py b/src/falconpy/oauth2.py index 1fe717b99..6d11b7d64 100644 --- a/src/falconpy/oauth2.py +++ b/src/falconpy/oauth2.py @@ -80,7 +80,8 @@ def __init__(self, debug: Optional[bool] = False, debug_record_count: Optional[int] = None, sanitize_log: Optional[bool] = None, - pythonic: Optional[bool] = None + pythonic: Optional[bool] = None, + environment: Optional[Dict[str, str]] = None ): """Construct an instance of the class. @@ -135,7 +136,8 @@ class (OAuth2) debug=debug, # |o debug_record_count=debug_record_count, sanitize_log=sanitize_log, - pythonic=pythonic + pythonic=pythonic, + environment=environment ) def logout(self) -> Dict[str, Union[int, dict]]: diff --git a/src/falconpy/sample_uploads.py b/src/falconpy/sample_uploads.py index f0b6807b3..a8428eea2 100644 --- a/src/falconpy/sample_uploads.py +++ b/src/falconpy/sample_uploads.py @@ -252,6 +252,11 @@ def upload_archive(self: object, # Create a multipart form payload for our upload file file_tuple = [("file", (name, file_data, content_type))] file_extended = {"name": name} + if kwargs.get("password", None): + file_extended["password"] = kwargs.get("password") + if kwargs.get("is_confidential", None): + file_extended["is_confidential"] = kwargs.get("is_confidential") + return process_service_request( calling_object=self, endpoints=Endpoints, diff --git a/src/falconpy/sensor_download.py b/src/falconpy/sensor_download.py index cee590223..3645c1a19 100644 --- a/src/falconpy/sensor_download.py +++ b/src/falconpy/sensor_download.py @@ -88,6 +88,40 @@ def get_combined_sensor_installers_by_query(self: object, params=parameters ) + @force_default(defaults=["parameters"], default_types=["dict"]) + def get_combined_sensor_installers_by_query_v2(self: object, + parameters: dict = None, + **kwargs + ) -> Dict[str, Union[int, dict]]: + """Retrieve all metadata for installers from provided query. + + Also provides architectural details. + + Keyword arguments: + filter -- The filter expression that should be used to limit the results. FQL syntax. + limit -- The maximum number of records to return. [integer, 1-5000] + offset -- The first item to return, where 0 is the latest item. (Integer) + Use with the limit parameter to manage pagination of results. + parameters - full parameters payload, not required if using other keywords. + sort -- The property to sort by. FQL syntax (e.g. status.desc or hostname.asc). + + This method only supports keywords for providing arguments. + + Returns: dict object containing API response. + + HTTP Method: GET + + Swagger URL + https://assets.falcon.crowdstrike.com/support/api/swagger.html#/sensor-download/GetCombinedSensorInstallersByQueryV2 + """ + return process_service_request( + calling_object=self, + endpoints=Endpoints, + operation_id="GetCombinedSensorInstallersByQueryV2", + keywords=kwargs, + params=parameters + ) + @force_default(defaults=["parameters"], default_types=["dict"]) def download_sensor_installer(self: object, *args, @@ -133,6 +167,51 @@ def download_sensor_installer(self: object, return returned + @force_default(defaults=["parameters"], default_types=["dict"]) + def download_sensor_installer_v2(self: object, + *args, + parameters: dict = None, + file_name: str = None, + download_path: str = None, + **kwargs) -> object: + """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. + + Keyword arguments: + download_path -- path to the folder to save installer file. + Must be present to cause a file download. + id -- SHA256 of the installer to download. + file_name -- name to use for saved file. Must be present to cause a file download. + parameters - full parameters payload, not required if id is provided as a keyword. + + Arguments: When not specified, the first argument to this method is assumed to be 'ids'. + All others are ignored. + + Returns: binary object on SUCCESS, dict object containing API response on FAILURE. + + HTTP Method: GET + + Swagger URL + https://assets.falcon.crowdstrike.com/support/api/swagger.html#/sensor-download/DownloadSensorInstallerByIdV2 + """ + returned = process_service_request( + calling_object=self, + endpoints=Endpoints, + operation_id="DownloadSensorInstallerByIdV2", + keywords=kwargs, + params=handle_single_argument(args, parameters, "ids") + ) + if file_name and download_path and isinstance(returned, bytes): + os.makedirs(download_path, exist_ok=True) + # write the newly downloaded sensor into the + # aforementioned directory with provided file name + with open(os.path.join(download_path, file_name), "wb") as sensor: + sensor.write(returned) + returned = generate_ok_result(message="Download successful") + + return returned + @force_default(defaults=["parameters"], default_types=["dict"]) def get_sensor_installer_entities(self: object, *args, @@ -165,6 +244,38 @@ def get_sensor_installer_entities(self: object, params=handle_single_argument(args, parameters, "ids") ) + @force_default(defaults=["parameters"], default_types=["dict"]) + def get_sensor_installer_entities_v2(self: object, + *args, + parameters: dict = None, + **kwargs + ) -> object: + """For a given list of SHA256's, retrieve the metadata for each installer. + + (Examples: release_date, version). + + Keyword arguments: + ids -- List of SHA256s for installers to retrieve details for. String or list of strings. + parameters - full parameters payload, not required if ids is provided as a keyword. + + Arguments: When not specified, the first argument to this method is assumed to be 'ids'. + All others are ignored. + + Returns: dict object containing API response. + + HTTP Method: GET + + Swagger URL + https://assets.falcon.crowdstrike.com/support/api/swagger.html#/sensor-download/GetSensorInstallersEntitiesV2 + """ + return process_service_request( + calling_object=self, + endpoints=Endpoints, + operation_id="GetSensorInstallersEntitiesV2", + keywords=kwargs, + params=handle_single_argument(args, parameters, "ids") + ) + def get_sensor_installer_ccid(self: object) -> Dict[str, Union[int, dict]]: """Retrieve the CID for the current oauth environment. @@ -212,14 +323,47 @@ def get_sensor_installers_by_query(self: object, parameters: dict = None, **kwar params=parameters ) + @force_default(defaults=["parameters"], default_types=["dict"]) + def get_sensor_installers_by_query_v2(self: object, parameters: dict = None, **kwargs) -> Dict[str, Union[int, dict]]: + """Retrieve a list of SHA256 for installers based on the filter. + + Keyword arguments: + filter -- The filter expression that should be used to limit the results. FQL syntax. + limit -- The maximum number of records to return. [integer, 1-500] + offset -- The first item to return, where 0 is the latest item. (Integer) + Use with the limit parameter to manage pagination of results. + parameters - full parameters payload, not required if using other keywords. + sort -- The property to sort by. FQL syntax (e.g. version|ASC, release_date|DESC). + + This method only supports keywords for providing arguments. + + Returns: dict object containing API response. + + HTTP Method: GET + + Swagger URL + https://assets.falcon.crowdstrike.com/support/api/swagger.html#/sensor-download/GetSensorInstallersByQueryV2 + """ + return process_service_request( + calling_object=self, + endpoints=Endpoints, + operation_id="GetSensorInstallersByQueryV2", + keywords=kwargs, + params=parameters + ) + # These method names align to the operation IDs in the API but # do not conform to snake_case / PEP8 and are defined here for # backwards compatibility / ease of use purposes GetCombinedSensorInstallersByQuery = get_combined_sensor_installers_by_query + GetCombinedSensorInstallersByQueryV2 = get_combined_sensor_installers_by_query_v2 DownloadSensorInstallerById = download_sensor_installer + DownloadSensorInstallerByIdV2 = download_sensor_installer_v2 GetSensorInstallersEntities = get_sensor_installer_entities + GetSensorInstallersEntitiesV2 = get_sensor_installer_entities_v2 GetSensorInstallersCCIDByQuery = get_sensor_installer_ccid GetSensorInstallersByQuery = get_sensor_installers_by_query + GetSensorInstallersByQueryV2 = get_sensor_installers_by_query_v2 # The legacy name for this class does not conform to PascalCase / PEP8 diff --git a/src/falconpy/workflows.py b/src/falconpy/workflows.py index 7ad182a44..299e5ca28 100644 --- a/src/falconpy/workflows.py +++ b/src/falconpy/workflows.py @@ -48,7 +48,8 @@ workflow_deprovision_payload, workflow_template_payload, workflow_definition_payload, - workflow_human_input + workflow_human_input, + workflow_mock_payload ) from ._service_class import ServiceClass from ._endpoint._workflows import _workflows_endpoints as Endpoints @@ -132,6 +133,7 @@ def export_definition(self: object, *args, parameters: dict = None, **kwargs) -> Keyword arguments: id -- ID of workflow definitions to return details for. String. parameters -- Full parameters payload dictionary. Not required if using other keywords. + sanitize -- Sanitize PII from workflow before it's exported. Boolean. Arguments: When not specified, the first argument to this method is assumed to be 'id'. All others are ignored. @@ -225,21 +227,26 @@ def update_definition(self: object, ) @force_default(defaults=["body", "parameters"], default_types=["dict", "dict"]) - def create_definition(self: object, - body: dict = None, - parameters: dict = None, - **kwargs - ) -> Dict[str, Union[int, dict]]: - """Create a workflow definition based on the provided model. + def execute(self: object, body: dict = None, parameters: dict = None, **kwargs) -> Dict[str, Union[int, dict]]: + """Execute an on-demand workflow. Response will contain the execution ID. Keyword arguments: - validate_only -- When enabled, prevents saving workflow after validating. Boolean. - body -- Full body payload in JSON format, not required when using other keywords. - definition -- Full workflow definition. Dictionary. - change_log -- Optional description to outline changes made during the update. String. - enabled -- Specifies if the new definition should be enabled upon creation. - flight_control -- Flight control parameters. Dictionary. - parameters -- Full parameters payload dictionary. Not required if using other keywords. + body -- full body payload, not required if using other keywords. + { + Workflow schema + } + definition_id -- Definition ID to execute. Either a name or ID can be specified. + String or List of Strings. + execution_cid -- CID(s) to execute on. This can be a child for Flight Control scenarios. + If unset, the definition CID is used. String or List of strings. + name -- Workflow name to execute. Either a name or ID can be specified. String. + parameters -- Full parameters payload in dictionary (JSON) format. Not required + if you are using other keywords. Dictionary. + key -- Key used to help deduplicate executions. If unset a new UUID is used. String. + depth -- Used to record the execution depth to help limit execution loops when a workflow + triggers another. The maximum depth is 4. Integer. + source_event_url -- Used to record a URL to the source that led to trigger the workflow. + String. This method only supports keywords for providing arguments. @@ -248,25 +255,23 @@ def create_definition(self: object, HTTP Method: POST Swagger URL - https://assets.falcon.crowdstrike.com/support/api/swagger.html#/workflows/WorkflowDefinitionsCreate + https://assets.falcon.crowdstrike.com/support/api/swagger.html#/workflows/WorkflowExecute """ - if not body: - body = workflow_definition_payload(passed_keywords=kwargs) - return process_service_request( calling_object=self, endpoints=Endpoints, - operation_id="WorkflowDefinitionsCreate", + operation_id="WorkflowExecute", keywords=kwargs, params=parameters, body=body ) @force_default(defaults=["body", "parameters"], default_types=["dict", "dict"]) - def execute(self: object, body: dict = None, parameters: dict = None, **kwargs) -> Dict[str, Union[int, dict]]: + def execute_internal(self: object, body: dict = None, parameters: dict = None, **kwargs) -> Dict[str, Union[int, dict]]: """Execute an on-demand workflow. Response will contain the execution ID. Keyword arguments: + batch_size -- Used to set the size of the batch. Integer. body -- full body payload, not required if using other keywords. { Workflow schema @@ -291,12 +296,60 @@ def execute(self: object, body: dict = None, parameters: dict = None, **kwargs) HTTP Method: POST Swagger URL - https://assets.falcon.crowdstrike.com/support/api/swagger.html#/workflows/WorkflowExecute + https://assets.falcon.crowdstrike.com/support/api/swagger.html#/workflows/WorkflowExecuteInternal """ return process_service_request( calling_object=self, endpoints=Endpoints, - operation_id="WorkflowExecute", + operation_id="WorkflowExecuteInternal", + keywords=kwargs, + params=parameters, + body=body + ) + + @force_default(defaults=["body", "parameters"], default_types=["dict", "dict"]) + def mock_execute(self: object, body: dict = None, parameters: dict = None, **kwargs) -> Dict[str, Union[int, dict]]: + """Execute a workflow definition with mocks. + + Keyword arguments: + body -- full body payload, not required if using other keywords. + { + "definition" { + Workflow schema + }, + "mocks": "string", + "on_demand_trigger": "string" + } + definition_id -- Definition ID to execute. Either a name or ID can be specified. + String or List of Strings. + execution_cid -- CID(s) to execute on. This can be a child for Flight Control scenarios. + If unset, the definition CID is used. String or List of strings. + name -- Workflow name to execute. Either a name or ID can be specified. String. + parameters -- Full parameters payload in dictionary (JSON) format. Not required + if you are using other keywords. Dictionary. + key -- Key used to help deduplicate executions. If unset a new UUID is used. String. + depth -- Used to record the execution depth to help limit execution loops when a workflow + triggers another. The maximum depth is 4. Integer. + source_event_url -- Used to record a URL to the source that led to trigger the workflow. + String. + validate_only -- PRevent execution after validating mocks against definition. Boolean. + + This method only supports keywords for providing arguments. + + Returns: dict object containing API response. + + HTTP Method: POST + + Swagger URL + https://assets.falcon.crowdstrike.com/support/api/swagger.html#/workflows/WorkflowMockExecute + """ + if not body: + body = workflow_mock_payload(passed_keywords=kwargs) + + return process_service_request( + calling_object=self, + endpoints=Endpoints, + operation_id="WorkflowMockExecute", keywords=kwargs, params=parameters, body=body @@ -651,8 +704,9 @@ def provision(self: object, body: dict = None, **kwargs) -> Dict[str, Union[int, WorkflowDefinitionsExport = export_definition WorkflowDefinitionsImport = import_definition WorkflowDefinitionsUpdate = update_definition - WorkflowDefinitionsCreate = create_definition WorkflowExecute = execute + WorkflowExecuteInternal = execute_internal + WorkflowMockExecute = mock_execute WorkflowExecutionsAction = execution_action WorkflowExecutionResults = execution_results WorkflowGetHumanInputV1 = get_human_input diff --git a/tests/test_authentications.py b/tests/test_authentications.py index 1af019487..879dc7cf5 100644 --- a/tests/test_authentications.py +++ b/tests/test_authentications.py @@ -18,7 +18,16 @@ # 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 ZeroTrustAssessment, CloudConnectAWS, OAuth2, APIHarness, version, InvalidCredentialFormat, Hosts +from falconpy import ( + ZeroTrustAssessment, + CloudConnectAWS, + OAuth2, + APIHarness, + version, + InvalidCredentialFormat, + Hosts, + Detects + ) from falconpy._util import confirm_base_region from falconpy._version import _TITLE, _VERSION @@ -51,12 +60,12 @@ def serviceAny_TestBadCredRevoke(self): return False def serviceAny_TestStaleObjectAuth(self): - falcon = CloudConnectAWS(auth_object=OAuth2(creds={"client_id": auth.config["falcon_client_id"], + falcon = Detects(auth_object=OAuth2(creds={"client_id": auth.config["falcon_client_id"], "client_secret": auth.config["falcon_client_secret"] }, base_url = "us-1", # Testing dashed base specifier debug=_DEBUG)) - result = falcon.QueryAWSAccounts() + result = falcon.QueryDetects() if result["status_code"] in AllowedResponses: return True else: @@ -140,12 +149,12 @@ def serviceAny_forceGovCloudAutoSelectFailure(self): } result = falcon.command("oauth2AccessToken", data=t_creds, base_url="usgov1") if result["status_code"] == 201: - falcon = CloudConnectAWS(client_id=os.environ["CROSS_DEBUG_KEY"], - client_secret=os.environ["CROSS_DEBUG_SECRET"], - base_url="usgov1", - renew_window=300, - debug=_DEBUG - ) + falcon = Detects(client_id=os.environ["CROSS_DEBUG_KEY"], + client_secret=os.environ["CROSS_DEBUG_SECRET"], + base_url="usgov1", + renew_window=300, + debug=_DEBUG + ) result = falcon.auth_object.token() if result["status_code"] == 429: pytest.skip("Rate limit hit") @@ -167,8 +176,8 @@ def serviceAny_TestObjectAuth(self): ) auth_obj.token() # While we're at it, test user_agent override - falcon = CloudConnectAWS(auth_object=auth_obj, user_agent=f"{_TITLE}/{str(_VERSION)}", debug=_DEBUG) - result = falcon.QueryAWSAccounts() + falcon = Detects(auth_object=auth_obj, user_agent=f"{_TITLE}/{str(_VERSION)}", debug=_DEBUG) + result = falcon.QueryDetects() if result["status_code"] not in AllowedResponses: _returned = False # And test the new built in logout functionality @@ -200,8 +209,8 @@ def serviceAny_TestObjectAuth(self): def serviceAny_TestBadObjectAuth(self): # Should also test bad direct auth in the authentication class - falcon = CloudConnectAWS(auth_object=OAuth2(debug=_DEBUG)) - result = falcon.QueryAWSAccounts() + falcon = Detects(auth_object=OAuth2(debug=_DEBUG)) + result = falcon.QueryDetects() if result["status_code"] in AllowedResponses: return True else: @@ -215,8 +224,8 @@ def serviceAny_TestEasyObjectAuth(self): # auth_obj.token() # Test passing just the service class object, not the auth_object attribute # Service Class base object should detect and handle this. - falcon = CloudConnectAWS(auth_object=auth_obj) - result = falcon.QueryAWSAccounts() + falcon = Detects(auth_object=auth_obj) + result = falcon.QueryDetects() if result["status_code"] in AllowedResponses: return True else: @@ -294,7 +303,12 @@ def test_EnvironmentAuthentication(self): save_id = os.getenv("FALCON_CLIENT_ID") save_key = os.getenv("FALCON_CLIENT_SECRET") if save_id or save_key: - thing = Hosts(debug=_DEBUG) + env_keys = { + "id_name": "DEBUG_API_ID", + "secret_name": "DEBUG_API_SECRET", + "prefix": "" + } + thing = Hosts(debug=_DEBUG, environment=env_keys) result = thing.login() if thing.token_status == 201: _returned = True diff --git a/tests/test_cloud_connect_aws.py b/tests/test_cloud_connect_aws.py index 75b23ef7c..70230d84a 100644 --- a/tests/test_cloud_connect_aws.py +++ b/tests/test_cloud_connect_aws.py @@ -32,6 +32,10 @@ if _DEBUG: _DEBUG = True +usone_only = pytest.mark.skipif(falcon.base_url.lower() != "https://api.crowdstrike.com", + reason="US-1 unit testing only", + ) + class TestCloudConnectAWS: def serviceCCAWS_AuthWithCreds(self): falconWithCreds = CloudConnectAWS(creds={ @@ -169,12 +173,15 @@ def serviceCCAWS_GenerateErrors(self): return errorChecks + @usone_only def test_GetAWSSettings(self): assert bool(falcon.GetAWSSettings()["status_code"] in AllowedResponses) is True + @usone_only def test_QueryAWSAccounts(self): assert bool(falcon.QueryAWSAccounts(parameters={"limit": 1})["status_code"] in AllowedResponses) is True + @usone_only @pytest.mark.skipif(falcon.QueryAWSAccounts( parameters={"limit": 1} )["status_code"] == 429, reason="API rate limit reached") @@ -186,32 +193,41 @@ def test_GetAWSAccounts(self): test_id = "123456789012" assert bool(falcon.GetAWSAccounts(ids=test_id)["status_code"] in AllowedResponses) is True + @usone_only @pytest.mark.skipif(falcon.QueryAWSAccounts( parameters={"limit": 1} )["status_code"] == 429, reason="API rate limit reached") def test_GetAWSAccountsUsingList(self): assert self.serviceCCAWS_GetAWSAccountsUsingList() is True + @usone_only def test_QueryAWSAccountsForIDs(self): assert bool(falcon.QueryAWSAccountsForIDs(parameters={"limit": 1})["status_code"] in AllowedResponses) is True + @usone_only def test_AuthWithCreds(self): assert self.serviceCCAWS_AuthWithCreds() is True + @usone_only def test_AuthWithObject(self): assert self.serviceCCAWS_AuthWithObject() is True + @usone_only def test_RefreshToken(self): assert self.serviceCCAWS_RefreshToken() is True + @usone_only def test_InvalidPayloads(self): assert self.serviceCCAWS_InvalidPayloads() is True + @usone_only def test_ForceAttributeError(self): assert self.serviceCCAWS_ForceAttributeError() is True + @usone_only def test_argument_vs_keyword(self): assert bool(falcon.get_aws_accounts("123456789012")["status_code"] in AllowedResponses) is True + @usone_only def test_Errors(self): assert self.serviceCCAWS_GenerateErrors() is True diff --git a/tests/test_cloud_snapshots.py b/tests/test_cloud_snapshots.py index 669131d7f..58285c053 100644 --- a/tests/test_cloud_snapshots.py +++ b/tests/test_cloud_snapshots.py @@ -25,10 +25,10 @@ def run_tests(self): tests = { "RegisterAccount": falcon.register_account(aws_accounts=[{"account_number": "1"}]), "RegisterAccountToo": falcon.register_account(account_number="12345678"), - "CreateInventory": falcon.create_inventory(job_metadata={"cloud_provider": "aws"}, result=[{"major_version": "1"}]), - "CreateInventoryAlso": falcon.create_inventory(cloud_provider="aws", major_version="1", os_version="12"), - "CreateInventoryAsWell": falcon.create_inventory(results={"applications":[]}), - "CreateInventoryTheFourth": falcon.create_inventory(applications=[{"major_version": "42"}]) + "ReadDeploymentsCombined": falcon.search_scan_jobs(), + "ReadDeploymentEntities": falcon.get_scan_jobs(ids="ABCDEF12-1234-ABCD-5678-ABCDEF123456"), + "GetScanReport": falcon.get_scan_reports(ids="12345678"), + "CreateDeploymentEntity": falcon.launch_scan_job(account_id="12345", asset_identifier="123456", cloud_provider="aws", region="us-east-2") } for key in tests: if tests[key]["status_code"] not in AllowedResponses: diff --git a/tests/test_container_detections.py b/tests/test_container_detections.py index 4ac50e660..1481a4e3a 100644 --- a/tests/test_container_detections.py +++ b/tests/test_container_detections.py @@ -27,6 +27,7 @@ def test_all_code_paths(self): "ReadDetectionsCountByType": falcon.read_detections_count_by_type(filter="cid:'1234567890'"), "ReadDetectionsCount": falcon.read_detections_count(filter="cid:'1234567890'"), "ReadCombinedDetections": falcon.read_combined_detections(filter="cid:'1234567890'", limit=1), + "GetRuntimeDetectionsCombinedV2": falcon.search_runtime_detections(limit=1, filter="cid:'1234567'"), "ReadDetections": falcon.read_detections(limit=1, filter="cid:'1234567890'"), "SearchDetections": falcon.search_detections(filter="cid:'1234567890'", limit=1) } diff --git a/tests/test_cspm_registration.py b/tests/test_cspm_registration.py index 190307a65..65e3c997a 100644 --- a/tests/test_cspm_registration.py +++ b/tests/test_cspm_registration.py @@ -95,7 +95,11 @@ def cspm_generate_errors(self): "UpdateGCP": falcon.update_gcp_account(environment="temperate", parent_id="1234567"), "GetMgmt": falcon.get_azure_management_group(tenant_id="1234567"), "CreateMgmt": falcon.create_azure_management_group(default_subscription_id="bob", tenant_id="1234567"), - "UpdateAzure": falcon.update_azure_account(environment="chilly", subscription_id="banana") + "DeleteAzureManagementGroup": falcon.delete_azure_management_group("whatever_tenant_ID"), + "UpdateAzure": falcon.update_azure_account(environment="chilly", subscription_id="banana"), + "ValidateGCPAccount": falcon.validate_gcp_account("whatever_account_id"), + "ValidateGCPServiceAccount": falcon.validate_gcp_service_account(resources=[{"service_account_id": 1}]), + "ValidateGCPServiceAccountToo": falcon.validate_gcp_service_account(service_account_id=1) } for key in tests: if tests[key]["status_code"] != 500: diff --git a/tests/test_discover.py b/tests/test_discover.py index 990cd9efe..666c717fd 100644 --- a/tests/test_discover.py +++ b/tests/test_discover.py @@ -50,6 +50,7 @@ def run_all_tests(self): if check["body"]["resources"]: logins_id_list = check["body"]["resources"] check = falcon.query_iot_hosts(limit=1) + check = falcon.query_iot_hosts_v2(limit=1) assets_id_list = "1234567890" if check["status_code"] == 429: pytest.skip("Rate limit hit") diff --git a/tests/test_falcon_complete_dashboard.py b/tests/test_falcon_complete_dashboard.py index 1e0410b5f..714c4d4de 100644 --- a/tests/test_falcon_complete_dashboard.py +++ b/tests/test_falcon_complete_dashboard.py @@ -87,6 +87,7 @@ def ServiceFCD_GenerateErrors(self): "AggregateRemediations": falcon.aggregate_remediations(), "AggregatePreventionPolicy": falcon.aggregate_prevention_policy(), "AggregateSensorUpdatePolicy": falcon.aggregate_sensor_update_policy(), + "AggregateSupportIssues": falcon.aggregate_support_issues(), "AggregateTotalDeviceCounts": falcon.aggregate_total_device_counts() } diff --git a/tests/test_foundry_logscale.py b/tests/test_foundry_logscale.py index 97972ca59..5c61a0b2e 100644 --- a/tests/test_foundry_logscale.py +++ b/tests/test_foundry_logscale.py @@ -31,6 +31,8 @@ def run_all_tests(self): "CreateSavedSearchesExecuteV1" : falcon.execute(search_parameters={"something": "somethingElse"}, end="10", start="1"), "CreateSavedSearchesIngestV1" : falcon.populate(app_id="pommegranate"), "GetSavedSearchesJobResultsDownloadV1" : falcon.download_results(job_id="12345", result_format="json"), + "IngestDataAsyncV1": falcon.ingest_data_async(data_file="testfile.png", tag="file_tag"), + "IngestDataAsyncV1variant": falcon.ingest_data_async(file="testfile.png", tag="file_tag") } for key in tests: if tests[key]["status_code"] not in AllowedResponses: diff --git a/tests/test_intel.py b/tests/test_intel.py index 29e377723..a94518f2e 100644 --- a/tests/test_intel.py +++ b/tests/test_intel.py @@ -37,7 +37,7 @@ def intel_test_all_code_paths(self): "query_intel_rule_ids": falcon.QueryIntelRuleIds(parameters={"type": "common-event-format"}), "query_mitre_attacks": falcon.QueryMitreAttacks("fancy-bear"), "mitre_attacks": falcon.PostMitreAttacks(["fancy-bear", "slippy-spider"]), - "get_mitre_report": falcon.GetMitreReport(actor_id="fancy-bear", format="CSV") + "get_mitre_report": falcon.GetMitreReport(actor_id="fancy-bear", format="CSV"), # "get_vulnerabilities": falcon.get_vulnerabilities(ids="12345678"), # "query_vulnerabilities": falcon.query_vulnerabilities() } @@ -46,6 +46,10 @@ def intel_test_all_code_paths(self): # US-1 only for now. tests["get_vulnerabilities"] = falcon.get_vulnerabilities(ids="12345678") tests["query_vulnerabilities"] = falcon.query_vulnerabilities() + tests["get_malware_entities"] = falcon.get_malware_entities("fancy-bear") + tests["query_mitre_attacks_for_malware"] = falcon.query_mitre_attacks_for_malware(ids="fancy-bear") + tests["query_malware"] = falcon.query_malware(limit=1) + for key in tests: if isinstance(tests[key], dict): # Allow for GetMitreReport's binary response if tests[key]["status_code"] not in AllowedResponses: diff --git a/tests/test_result_object.py b/tests/test_result_object.py index dc93cf780..af5711e21 100644 --- a/tests/test_result_object.py +++ b/tests/test_result_object.py @@ -730,7 +730,10 @@ def test_batch_init_id_without_a_session(self): def test_unusual_response_formatting(self): _returned = False cspm = CSPMRegistration(auth_object=config) - result = cspm.get_configuration_detections(limit=1)["body"]["resources"] + try: + result = cspm.get_configuration_detections(limit=1)["body"]["resources"] + except KeyError: + result = Result(status_code=200, headers={}, body={"something": "different"}).full_return if isinstance(result, dict): _returned = True assert _returned @@ -753,6 +756,7 @@ def test_unnecessary_encoding_used_warning(self): reason="Unit testing unavailable on US-GOV-1" ) @not_supported + @pytest.mark.skipif("us-1" not in config.base_url, reason="This unit test is only supported in US-1.") def test_pythonic_deprecation_warnings(self): _success = False with pytest.warns(SDKDeprecationWarning): diff --git a/tests/test_sample_uploads.py b/tests/test_sample_uploads.py index 04192cf77..2889f26df 100644 --- a/tests/test_sample_uploads.py +++ b/tests/test_sample_uploads.py @@ -113,8 +113,8 @@ def sample_errors(self): file_type="zip" ), "ArchiveUploadV1b": falcon.ArchiveUploadV1(name="FalconPy testing", body=None), - "ArchiveUploadV2": falcon.ArchiveUploadV2(name="testfile.zip", archive=PAYLOAD, source="workstation", comment="FalconPy testing"), - "ArchiveUploadV2b": falcon.ArchiveUploadV2(file=PAYLOAD, source="workstation", comment="FalconPy testing"), + "ArchiveUploadV2": falcon.ArchiveUploadV2(name="testfile.zip", archive=PAYLOAD, source="workstation", comment="FalconPy testing", password="whatever", is_confidential=True), + "ArchiveUploadV2": falcon.upload_archive(file=PAYLOAD, source="workstation", comment="FalconPy testing"), "ExtractionListV1": falcon.ExtractionListV1(id="12345779"), "ExtractionGetV1": falcon.ExtractionGetV1(ids="12345678"), "ExtractionCreateV1": falcon.ExtractionCreateV1(extract_all=True, files=[{ diff --git a/tests/test_sensor_download.py b/tests/test_sensor_download.py index c5407fdaf..3a7cf0871 100644 --- a/tests/test_sensor_download.py +++ b/tests/test_sensor_download.py @@ -28,7 +28,7 @@ def _get_cid(): def _get_multiple_shas(): params = {"filter": 'platform:"linux"', "sort": "release_date|desc"} try: - shas = sensor_download_client.GetSensorInstallersByQuery(parameters=params)["body"]["resources"] + shas = sensor_download_client.GetSensorInstallersByQueryV2(parameters=params)["body"]["resources"] except KeyError: # Workflow download error, skip it shas = True @@ -36,22 +36,31 @@ def _get_multiple_shas(): return shas - def _download_sensor(self): + def _download_sensor(self, style = "v2"): sha_id = self._get_multiple_shas()[0] - resp = sensor_download_client.DownloadSensorInstallerById(parameters={"id": sha_id}) + if style == "v1": + resp = sensor_download_client.DownloadSensorInstallerById(parameters={"id": sha_id}) + else: + resp = sensor_download_client.DownloadSensorInstallerByIdV2(parameters={"id": sha_id}) if isinstance(resp, bytes): return True else: return False - def _download_sensor_file(self): + def _download_sensor_file(self, style = "v2"): file_name = "sensor.rpm" directory_path = "." sha_id = self._get_multiple_shas()[0] - _ = sensor_download_client.DownloadSensorInstallerById(parameters={"id": sha_id}, - file_name=file_name, - download_path=directory_path - ) + if style == "v1": + _ = sensor_download_client.DownloadSensorInstallerById(parameters={"id": sha_id}, + file_name=file_name, + download_path=directory_path + ) + else: + _ = sensor_download_client.DownloadSensorInstallerByIdV2(parameters={"id": sha_id}, + file_name=file_name, + download_path=directory_path + ) if os.path.exists("sensor.rpm"): os.remove("sensor.rpm") return True @@ -66,11 +75,24 @@ def _get_metadata_for_filter(): resp = sensor_download_client.GetCombinedSensorInstallersByQuery(filter='platform:"windows"', sort="release_date|desc") return True if resp["status_code"] in AllowedResponses else False + @staticmethod + def _get_metadata_for_filter_v2(): + # Testing new parameter functionality + # params = {"filter": 'platform:"windows"', "sort": "release_date|desc"} + # resp = sensor_download_client.GetCombinedSensorInstallersByQuery(parameters=params) + resp = sensor_download_client.GetCombinedSensorInstallersByQueryV2(filter='platform:"windows"', sort="release_date|desc") + return True if resp["status_code"] in AllowedResponses else False + def _get_metadata_for_ids(self): sha_ids = self._get_multiple_shas() resp = sensor_download_client.GetSensorInstallersEntities(ids=sha_ids) return True if resp["status_code"] in AllowedResponses else False + def _get_metadata_for_ids_v2(self): + sha_ids = self._get_multiple_shas() + resp = sensor_download_client.GetSensorInstallersEntitiesV2(ids=sha_ids) + return True if resp["status_code"] in AllowedResponses else False + @staticmethod def _get_all_metadata(): resp = sensor_download_client.GetCombinedSensorInstallersByQuery() @@ -82,14 +104,23 @@ def _get_all_metadata2(): return True if resp["status_code"] in AllowedResponses else False def test_download_windows_sensor(self): - assert self._download_sensor() is True + assert self._download_sensor(style="v1") is True def test_download_windows_sensor_file(self): + assert self._download_sensor_file(style="v1") is True + + def test_download_windows_sensor_v2(self): + assert self._download_sensor() is True + + def test_download_windows_sensor_file_v2(self): assert self._download_sensor_file() is True def test_get_sha_window_sensor(self): assert self._get_metadata_for_filter() is True + def test_get_sha_window_sensor_v2(self): + assert self._get_metadata_for_filter_v2() is True + def test_get_ccid(self): assert self._get_cid() is True @@ -99,6 +130,9 @@ def test_get_shas(self): def test_get_multiple_shas(self): assert self._get_metadata_for_ids() is True + def test_get_multiple_shas_v2(self): + assert self._get_metadata_for_ids_v2() is True + def test_get_all_metadata(self): assert self._get_all_metadata() is True diff --git a/tests/test_uber.py b/tests/test_uber.py index c905bca31..9bd474b09 100644 --- a/tests/test_uber.py +++ b/tests/test_uber.py @@ -156,7 +156,7 @@ def uberCCAWS_TestUploadDownload(self): return True def uberCCAWS_GenerateError(self): - if falcon.command("QueryAWSAccounts", partition=0)["status_code"] in AllowedResponses: + if falcon.command("QueryDetects", partition=0)["status_code"] in AllowedResponses: return True else: return False @@ -169,7 +169,7 @@ def uberCCAWS_GenerateInvalidPayload(self): return False def uberCCAWS_OverrideAndHeader(self): - if falcon.command(override="GET,/cloud-connect-aws/combined/accounts/v1", + if falcon.command(override="GET,/detects/queries/detects/v1", headers={"Nothing": "Special"})["status_code"] in AllowedResponses: return True else: @@ -195,7 +195,7 @@ def uberCCAWS_TestMSSP(self): return returned def uberCCAWS_BadMethod(self): - if falcon.command(action="", override="BANANA,/cloud-connect-aws/combined/accounts/v1", + if falcon.command(action="", override="BANANA,/detects/queries/detects/v1", headers={"Nothing": "Special"})["status_code"] in AllowedResponses: return True else: @@ -208,7 +208,7 @@ def uberCCAWS_BadCommand(self): return False def uberCCAWS_GenerateServerError(self): - if falcon.command("GetAWSAccounts", ids="123", data=['Kerash!'])["status_code"] == 500: + if falcon.command("GetDetectSummaries", ids="123", data=['Kerash!'])["status_code"] == 500: return True else: return False @@ -247,7 +247,7 @@ def uberCCAWS_GenerateTokenError(self): def uberCCAWS_BadAuthentication(self): falcon = APIHarnessV2(debug=_DEBUG) - if falcon.command("QueryAWSAccounts", parameters={"limit": 1})["status_code"] in AllowedResponses: + if falcon.command("QueryDetects", parameters={"limit": 1})["status_code"] in AllowedResponses: return True else: return False @@ -259,7 +259,7 @@ def uberCCAWS_DisableSSLVerify(self): "client_secret": config["falcon_client_secret"] }, ssl_verify=False, base_url=config["falcon_base_url"], debug=_DEBUG ) - if falcon.command("QueryAWSAccounts", parameters={"limit": 1})["status_code"] in AllowedResponses: + if falcon.command("QueryDetects", parameters={"limit": 1})["status_code"] in AllowedResponses: return True else: return False @@ -279,8 +279,8 @@ def uber_test_distinct_field(self): else: return False - def test_GetAWSSettings(self): - assert self.uberCCAWS_GetAWSSettings() is True + # def test_GetAWSSettings(self): + # assert self.uberCCAWS_GetAWSSettings() is True def test_reserved_words(self): assert self.uber_test_invalid_reserved_word_payload() is True @@ -288,16 +288,16 @@ def test_reserved_words(self): def test_distinct_field(self): assert self.uber_test_distinct_field() is True - def test_QueryAWSAccounts(self): - assert self.uberCCAWS_QueryAWSAccounts() is True + # def test_QueryAWSAccounts(self): + # assert self.uberCCAWS_QueryAWSAccounts() is True - @pytest.mark.skipif(falcon.command("QueryAWSAccounts", - parameters={"limit": 1})["status_code"] == 429, reason="API rate limit reached") - def test_GetAWSAccounts(self): - assert self.uberCCAWS_GetAWSAccounts() is True + # @pytest.mark.skipif(falcon.command("QueryAWSAccounts", + # parameters={"limit": 1})["status_code"] == 429, reason="API rate limit reached") + # def test_GetAWSAccounts(self): + # assert self.uberCCAWS_GetAWSAccounts() is True - def test_QueryAWSAccountsForIDs(self): - assert self.uberCCAWS_QueryAWSAccountsForIDs() is True + # def test_QueryAWSAccountsForIDs(self): + # assert self.uberCCAWS_QueryAWSAccountsForIDs() is True @pytest.mark.skipif("laggar" in falcon.base_url, reason="US-GOV-1 testing disabled") def test_UploadDownload(self): diff --git a/tests/test_uber_api_complete.py b/tests/test_uber_api_complete.py index 90f7c2cb9..670adb5a3 100644 --- a/tests/test_uber_api_complete.py +++ b/tests/test_uber_api_complete.py @@ -145,7 +145,7 @@ def uberCCAWS_TestUploadDownload(self): return True def uberCCAWS_GenerateError(self): - if falcon.command("QueryAWSAccounts", partition=0)["status_code"] in AllowedResponses: + if falcon.command("QueryDetects", partition=0)["status_code"] in AllowedResponses: return True else: return False @@ -158,7 +158,7 @@ def uberCCAWS_GenerateInvalidPayload(self): return False def uberCCAWS_OverrideAndHeader(self): - if falcon.command(override="GET,/cloud-connect-aws/combined/accounts/v1", + if falcon.command(override="GET,/detects/queries/detects/v1", headers={"Nothing": "Special"})["status_code"] in AllowedResponses: return True else: @@ -185,7 +185,7 @@ def uberCCAWS_TestMSSP(self): return returned def uberCCAWS_BadMethod(self): - if falcon.command(action="", override="BANANA,/cloud-connect-aws/combined/accounts/v1", + if falcon.command(action="", override="BANANA,/detects/queries/detects/v1", headers={"Nothing": "Special"})["status_code"] in AllowedResponses: return True else: @@ -198,7 +198,7 @@ def uberCCAWS_BadCommand(self): return False def uberCCAWS_GenerateServerError(self): - if falcon.command("GetAWSAccounts", ids="123", data=['Kerash!'])["status_code"] == 500: + if falcon.command("GetDetectSummaries", ids="123", data=['Kerash!'])["status_code"] == 500: return True else: return False @@ -237,7 +237,7 @@ def uberCCAWS_GenerateTokenError(self): def uberCCAWS_BadAuthentication(self): falcon = APIHarness(debug=_DEBUG) - if falcon.command("QueryAWSAccounts", parameters={"limit": 1})["status_code"] in AllowedResponses: + if falcon.command("QueryDetects", parameters={"limit": 1})["status_code"] in AllowedResponses: return True else: return False @@ -249,7 +249,7 @@ def uberCCAWS_DisableSSLVerify(self): "client_secret": config["falcon_client_secret"] }, ssl_verify=False, base_url=config["falcon_base_url"], debug=_DEBUG ) - if falcon.command("QueryAWSAccounts", parameters={"limit": 1})["status_code"] in AllowedResponses: + if falcon.command("QueryDetects", parameters={"limit": 1})["status_code"] in AllowedResponses: return True else: return False @@ -269,8 +269,8 @@ def uber_test_distinct_field(self): else: return False - def test_GetAWSSettings(self): - assert self.uberCCAWS_GetAWSSettings() is True + # def test_GetAWSSettings(self): + # assert self.uberCCAWS_GetAWSSettings() is True def test_reserved_words(self): assert self.uber_test_invalid_reserved_word_payload() is True @@ -278,16 +278,16 @@ def test_reserved_words(self): def test_distinct_field(self): assert self.uber_test_distinct_field() is True - def test_QueryAWSAccounts(self): - assert self.uberCCAWS_QueryAWSAccounts() is True + # def test_QueryAWSAccounts(self): + # assert self.uberCCAWS_QueryAWSAccounts() is True - @pytest.mark.skipif(falcon.command("QueryAWSAccounts", - parameters={"limit": 1})["status_code"] == 429, reason="API rate limit reached") - def test_GetAWSAccounts(self): - assert self.uberCCAWS_GetAWSAccounts() is True + # @pytest.mark.skipif(falcon.command("QueryAWSAccounts", + # parameters={"limit": 1})["status_code"] == 429, reason="API rate limit reached") + # def test_GetAWSAccounts(self): + # assert self.uberCCAWS_GetAWSAccounts() is True - def test_QueryAWSAccountsForIDs(self): - assert self.uberCCAWS_QueryAWSAccountsForIDs() is True + # def test_QueryAWSAccountsForIDs(self): + # assert self.uberCCAWS_QueryAWSAccountsForIDs() is True @pytest.mark.skipif("laggar" in falcon.base_url, reason="US-GOV-1 testing disabled") def test_UploadDownload(self): diff --git a/tests/test_workflows.py b/tests/test_workflows.py index b12adaf1d..cd1821e1d 100644 --- a/tests/test_workflows.py +++ b/tests/test_workflows.py @@ -23,6 +23,8 @@ def run_all_tests(self): error_checks = True tests = { "WorkflowExecute" : falcon.execute(body={}), + "WorkflowExecuteInternal" : falcon.execute_internal(body={}), + "WorkflowMockExecute" : falcon.mock_execute(body={}, mocks="whatever"), "WorkflowExecutionsAction" : falcon.execution_action(action_name="resume", ids="12345678"), "WorkflowExecutionResults" : falcon.execution_results(ids="12345678"), "WorkflowSystemDefinitionsDeProvision" : falcon.deprovision(definition_id="12345", deprovision_all=True), @@ -34,7 +36,6 @@ def run_all_tests(self): "WorkflowDefinitionsImport": falcon.import_definition(validate_only=True, data_file="this_will_415"), "WorkflowDefinitionsImport": falcon.import_definition(validate_only=True, file_data="this_will_500"), "WorkflowDefinitionsUpdate": falcon.update_definition(change_log="testing"), - "WorkflowDefinitionsCreate": falcon.create_definition(change_log="testing"), "WorkflowGetHumanInputV1": falcon.get_human_input(ids="1234567"), "WorkflowUpdateHumanInputV1": falcon.update_human_input(input="whatever", note="whatever"), }