falcon-toolkit 3.4.2__tar.gz → 3.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/PKG-INFO +2 -2
  2. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/falcon.py +2 -0
  3. falcon_toolkit-3.5.0/falcon_toolkit/users/__init__.py +4 -0
  4. falcon_toolkit-3.5.0/falcon_toolkit/users/cli.py +128 -0
  5. falcon_toolkit-3.5.0/falcon_toolkit/users/user_management.py +278 -0
  6. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/pyproject.toml +3 -2
  7. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/LICENSE.md +0 -0
  8. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/README.md +0 -0
  9. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/__init__.py +0 -0
  10. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/common/__init__.py +0 -0
  11. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/common/auth.py +0 -0
  12. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/common/auth_backends/__init__.py +0 -0
  13. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/common/auth_backends/default.py +0 -0
  14. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/common/auth_backends/public_mssp.py +0 -0
  15. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/common/auth_backends/public_single_cid.py +0 -0
  16. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/common/auth_backends/utils.py +0 -0
  17. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/common/cli.py +0 -0
  18. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/common/config.py +0 -0
  19. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/common/console_utils.py +0 -0
  20. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/common/constants.py +0 -0
  21. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/common/logging_config.py +0 -0
  22. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/common/meta.py +0 -0
  23. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/common/namespace.py +0 -0
  24. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/common/utils.py +0 -0
  25. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/containment/__init__.py +0 -0
  26. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/containment/cli.py +0 -0
  27. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/containment/perform_containment.py +0 -0
  28. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/hosts/__init__.py +0 -0
  29. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/hosts/cli.py +0 -0
  30. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/hosts/host_search.py +0 -0
  31. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/maintenance_token/__init__.py +0 -0
  32. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/maintenance_token/cli.py +0 -0
  33. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/maintenance_token/device_tokens.py +0 -0
  34. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/policies/__init__.py +0 -0
  35. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/policies/cli.py +0 -0
  36. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/policies/constants.py +0 -0
  37. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/policies/container.py +0 -0
  38. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/policies/describe.py +0 -0
  39. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/shell/__init__.py +0 -0
  40. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/shell/cli.py +0 -0
  41. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/shell/cmd_generators/__init__.py +0 -0
  42. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/shell/cmd_generators/common.py +0 -0
  43. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/shell/cmd_generators/reg.py +0 -0
  44. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/shell/parsers.py +0 -0
  45. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/shell/prompt.py +0 -0
  46. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/shell/refresh.py +0 -0
  47. {falcon_toolkit-3.4.2 → falcon_toolkit-3.5.0}/falcon_toolkit/shell/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: falcon-toolkit
3
- Version: 3.4.2
3
+ Version: 3.5.0
4
4
  Summary: Toolkit to interface with CrowdStrike Falcon via the API
5
5
  Home-page: http://github.com/CrowdStrike/Falcon-Toolkit
6
6
  License: MIT
@@ -22,7 +22,7 @@ Classifier: Programming Language :: Python :: 3 :: Only
22
22
  Classifier: Topic :: Security
23
23
  Classifier: Topic :: System :: Shells
24
24
  Classifier: Topic :: Terminals
25
- Requires-Dist: caracara (>=0.7.0,<0.8.0)
25
+ Requires-Dist: caracara (>=0.8.0,<0.9.0)
26
26
  Requires-Dist: click (>=8.1.3,<9.0.0)
27
27
  Requires-Dist: click-option-group (>=0.5.6,<0.6.0)
28
28
  Requires-Dist: click-spinner (>=0.1.10,<0.2.0)
@@ -54,6 +54,7 @@ from falcon_toolkit.hosts.cli import cli_host_search
54
54
  from falcon_toolkit.maintenance_token.cli import cli_maintenance_token
55
55
  from falcon_toolkit.policies.cli import cli_policies
56
56
  from falcon_toolkit.shell.cli import cli_shell
57
+ from falcon_toolkit.users.cli import cli_users
57
58
 
58
59
 
59
60
  @click.group()
@@ -312,3 +313,4 @@ cli.add_command(cli_list_filters)
312
313
  cli.add_command(cli_maintenance_token)
313
314
  cli.add_command(cli_policies)
314
315
  cli.add_command(cli_shell)
316
+ cli.add_command(cli_users)
@@ -0,0 +1,4 @@
1
+ """Falcon Toolkit: User Management.
2
+
3
+ This sub-module contains the implementation of the falcon user management commands.
4
+ """
@@ -0,0 +1,128 @@
1
+ """Falcon Toolkit: User Modification.
2
+
3
+ This file contains the CLI options for the falcon user modification commands.
4
+ """
5
+
6
+ from os import path
7
+
8
+ import click
9
+ from caracara import Client
10
+ from prompt_toolkit import prompt
11
+
12
+ from falcon_toolkit.users.user_management import (
13
+ user_deletion,
14
+ add_csv_users,
15
+ add_single_user,
16
+ add_roles_to_user,
17
+ print_formatted_user_roles,
18
+ describe_existing_users,
19
+ )
20
+ from falcon_toolkit.common.cli import get_instance
21
+
22
+
23
+ @click.group(
24
+ name="users",
25
+ help="Manage Falcon User Creation and Deletion",
26
+ )
27
+ @click.pass_context
28
+ def cli_users(ctx: click.Context):
29
+ """Falcon Toolkit user management base command."""
30
+ instance = get_instance(ctx)
31
+ client: Client = instance.auth_backend.authenticate(ctx)
32
+ ctx.obj["client"] = client
33
+
34
+
35
+ @click.command(
36
+ name="add_user",
37
+ help="Create users via CLI inputs",
38
+ )
39
+ @click.pass_context
40
+ def add_user(ctx: click.Context):
41
+ """Create a new user in the Falcon tenant."""
42
+ single_email = prompt("Enter the new user's email address: ")
43
+ first_name = prompt(f"Enter {single_email}'s first name: ")
44
+ last_name = prompt(f"Enter {single_email}'s last name: ")
45
+ add_single_user(ctx.obj["client"], first_name, last_name, single_email)
46
+
47
+
48
+ @click.command(
49
+ name="import_users",
50
+ help="Create users from a CSV file formatted first_name,last_name,email_address",
51
+ )
52
+ @click.argument("csv_file")
53
+ @click.pass_context
54
+ def import_users(ctx: click.Context, csv_file):
55
+ """Create users and set their roles in the Falcon tenant from a CSV file."""
56
+ if path.isfile(csv_file):
57
+ add_csv_users(ctx.obj["client"], csv_file)
58
+ else:
59
+ click.echo(click.style("Error: Invalid File Path", bold=True, fg="red"))
60
+
61
+
62
+ @click.command(
63
+ name="delete_user",
64
+ help="Delete users via email address",
65
+ )
66
+ @click.option(
67
+ "-f",
68
+ "--file",
69
+ "file_name",
70
+ help="path to line-seperated file of user accounts to be deleted",
71
+ )
72
+ @click.option(
73
+ "-e",
74
+ "--user_email",
75
+ help="Email address of user to be deleted",
76
+ )
77
+ @click.pass_context
78
+ def delete_user(ctx: click.Context, file_name: str = None, user_email: str = None):
79
+ """Delete a user from the Falcon tenant by email address, or delete many using a file."""
80
+ email_list = []
81
+ if file_name is not None:
82
+ try:
83
+ with open(file_name, newline="", encoding="utf-8") as email_list_file:
84
+ email_list = email_list_file.read().splitlines()
85
+ user_deletion(ctx.obj["client"], email_list)
86
+ except FileNotFoundError:
87
+ click.echo(click.style("Error: Invalid File Path", bold=True, fg="red"))
88
+ elif user_email is not None:
89
+ email_list.append(user_email)
90
+ user_deletion(ctx.obj["client"], email_list)
91
+ else:
92
+ click.echo(
93
+ click.style(
94
+ "Please specify either a file or user email. "
95
+ "Try 'falcon users delete_user --help' for help.",
96
+ bold=True,
97
+ fg="red",
98
+ )
99
+ )
100
+
101
+
102
+ @click.command(name="describe_users", help="List all existing users in the cloud instance")
103
+ @click.pass_context
104
+ def describe_users(ctx: click.Context):
105
+ """Show all the user accounts in the Falcon tenant, and their respective roles."""
106
+ describe_existing_users(ctx.obj["client"])
107
+
108
+
109
+ @click.command(name="list_roles", help="List all available roles in the cloud instance")
110
+ @click.pass_context
111
+ def list_roles(ctx: click.Context):
112
+ """Show all the roles that exist within the Falcon tenant and may be assigned to users."""
113
+ print_formatted_user_roles(ctx.obj["client"])
114
+
115
+
116
+ @click.command(name="add_roles", help="Add roles via user email and role ID")
117
+ @click.pass_context
118
+ def add_roles(ctx: click.Context):
119
+ """Grant new roles to a user account in the Falcon tenant."""
120
+ add_roles_to_user(ctx.obj["client"])
121
+
122
+
123
+ cli_users.add_command(add_user)
124
+ cli_users.add_command(import_users)
125
+ cli_users.add_command(delete_user)
126
+ cli_users.add_command(describe_users)
127
+ cli_users.add_command(list_roles)
128
+ cli_users.add_command(add_roles)
@@ -0,0 +1,278 @@
1
+ """Falcon Toolkit: User Management.
2
+
3
+ This file contains the logic required to create and configure users.
4
+ """
5
+
6
+ import csv
7
+ import sys
8
+ from operator import itemgetter
9
+ from textwrap import TextWrapper
10
+ from typing import Dict, List, Optional, Set, Union
11
+
12
+ import click
13
+ import click_spinner
14
+ import tabulate
15
+ from caracara import Client
16
+ from caracara.common.csdialog import csradiolist_dialog
17
+ from caracara.common.exceptions import GenericAPIError
18
+ from prompt_toolkit import prompt
19
+ from prompt_toolkit.completion import FuzzyWordCompleter
20
+
21
+ from falcon_toolkit.hosts import host_search
22
+
23
+
24
+ def _tabulate_users( # pylint: disable=R0914
25
+ user_data: Union[List[Dict[str, str]], Dict[str, str]],
26
+ role_data: Optional[Dict[str, Dict]] = None,
27
+ ) -> None:
28
+ """Pretty print a list of users to screen in a tabular format."""
29
+ header_row = [
30
+ click.style("Name", bold=True, fg="blue"),
31
+ click.style("Email Address", bold=True, fg="blue"),
32
+ click.style("Status", bold=True, fg="blue"),
33
+ click.style("Creation Date", bold=True, fg="blue"),
34
+ click.style("Modification Date", bold=True, fg="blue"),
35
+ click.style("UUID", bold=True, fg="blue"),
36
+ click.style("Roles", bold=True, fg="blue"),
37
+ ]
38
+ table_rows = []
39
+
40
+ grouping_tag_wrap = TextWrapper()
41
+ grouping_tag_wrap.width = 40
42
+
43
+ sixteen_wrap = TextWrapper()
44
+ sixteen_wrap.width = 16
45
+
46
+ for extracted_dict in user_data:
47
+ email_address = extracted_dict["uid"]
48
+ name = str(extracted_dict["first_name"]) + " " + str(extracted_dict["last_name"])
49
+ creation_date = extracted_dict["created_at"]
50
+ modification_date = extracted_dict["updated_at"]
51
+ uuid = extracted_dict["uuid"]
52
+ if role_data:
53
+ roles_list = sorted(
54
+ [
55
+ role_data[x]["display_name"] if x in role_data else x
56
+ for x in extracted_dict["roles"]
57
+ ]
58
+ )
59
+ else:
60
+ roles_list = extracted_dict["roles"]
61
+
62
+ activation_status = extracted_dict["status"]
63
+ if activation_status == "active":
64
+ status = click.style("Active", fg="green")
65
+ else:
66
+ status = click.style(activation_status, fg="red")
67
+
68
+ string_roles = ""
69
+ for role in roles_list:
70
+ string_roles = string_roles + role + "\n"
71
+
72
+ row = [name, email_address, status, creation_date, modification_date, uuid, string_roles]
73
+ table_rows.append(row)
74
+
75
+ table_rows = sorted(table_rows, key=itemgetter(1, 0))
76
+
77
+ for row in table_rows:
78
+ host_search.vertically_align_middle(row)
79
+
80
+ table_rows.insert(0, header_row)
81
+
82
+ click.echo(
83
+ tabulate.tabulate(
84
+ table_rows,
85
+ tablefmt="fancy_grid",
86
+ )
87
+ )
88
+
89
+
90
+ def add_csv_users(client: Client, csv_name: str): # pylint: disable=R0914
91
+ """Create users based on a CSV formatted first_name,last_name,email_address."""
92
+ successful_uuid_set: Set[str] = set()
93
+
94
+ with open(csv_name, newline="", encoding="utf-8") as csv_file:
95
+ reader = csv.DictReader(csv_file)
96
+ if (
97
+ "first_name" not in reader.fieldnames
98
+ or "last_name" not in reader.fieldnames
99
+ or "email_address" not in reader.fieldnames
100
+ ):
101
+ raise KeyError(
102
+ "The CSV must contain at least the fields first_name, last_name, and email_address"
103
+ )
104
+
105
+ for row in reader:
106
+ first_name = row["first_name"]
107
+ last_name = row["last_name"]
108
+ email_address = row["email_address"]
109
+ uuid: Optional[str] = None
110
+
111
+ click.echo(
112
+ "Processing user: "
113
+ + click.style(f"{first_name} {last_name}", bold=True)
114
+ + " ("
115
+ + click.style(email_address, bold=True)
116
+ + ")"
117
+ )
118
+
119
+ try:
120
+ user_add_result = client.users.add_user(
121
+ first_name,
122
+ last_name,
123
+ email_address,
124
+ )
125
+ uuid = user_add_result["uuid"]
126
+ successful_uuid_set.add(uuid)
127
+
128
+ except GenericAPIError as bad_user_error:
129
+ print(click.style(bad_user_error, bold=True, fg="yellow"))
130
+ except KeyError:
131
+ print(click.style("The API response did not include a new user UUID", fg="red"))
132
+
133
+ if "roles" in row and row["roles"] is not None:
134
+ roles_str = row["roles"]
135
+ click.echo(f"Adding roles to user {email_address}: {roles_str}")
136
+
137
+ if not uuid:
138
+ uuid = client.users.get_uuid_by_email(email_address)
139
+
140
+ roles = roles_str.split(",")
141
+ if client.users.add_user_roles(uuid, roles):
142
+ successful_uuid_set.add(uuid)
143
+ else:
144
+ print(click.style(f"Role addition failed for {roles}", bold=True, fg="red"))
145
+
146
+ if not successful_uuid_set:
147
+ # Nothing succeeded
148
+ return
149
+
150
+ with click_spinner.spinner():
151
+ user_data = client.users.describe_users(user_uuids=list(successful_uuid_set))
152
+ role_data = client.users.describe_available_roles()
153
+
154
+ click.echo(click.style("Affected Users", bold=True))
155
+ _tabulate_users(list(user_data.values()), role_data)
156
+
157
+
158
+ def add_single_user(client: Client, first_name: str, last_name: str, email_address: str):
159
+ """Create a single user in the Falcon tenant."""
160
+ with click_spinner.spinner():
161
+ result = client.users.add_user(first_name, last_name, email_address)
162
+ user_data = client.users.describe_users(user_uuids=[result["uuid"]])
163
+
164
+ click.echo(click.style("New User", bold=True))
165
+ _tabulate_users(list(user_data.values()))
166
+
167
+
168
+ def delete_user_guardrails(email_addresses: list):
169
+ """Confirm via a visual Prompt Toolkit box whether the user really wants to delete a user."""
170
+ confirmation_options = [
171
+ (False, "Abort"),
172
+ (True, "Delete accounts"),
173
+ ]
174
+ prompt_text = "Are you sure you want to delete these accounts?\n" + "\n".join(email_addresses)
175
+ confirmation: bool = csradiolist_dialog(
176
+ title="Confirm User Deletion Action",
177
+ text=prompt_text,
178
+ values=confirmation_options,
179
+ ).run()
180
+
181
+ if confirmation:
182
+ click.echo(click.style("User confirmed action", bold=True, fg="green"))
183
+ return True
184
+
185
+ click.echo(click.style("Aborted!", bold=True, fg="red"))
186
+ return False
187
+
188
+
189
+ def user_deletion(client: Client, email_addresses: list):
190
+ """Validate email addresses, confirm deletion, and ultimately delete users."""
191
+ uuid_list = []
192
+ bad_emails = []
193
+ for i in email_addresses:
194
+ try:
195
+ uuid = client.users.get_uuid_by_email(i)
196
+ uuid_list.append(uuid)
197
+ except GenericAPIError:
198
+ click.echo(click.style(f"Invalid Email Address: {i}", bold=True, fg="red"))
199
+ bad_emails.append(i)
200
+
201
+ good_emails = [email for email in email_addresses if email not in bad_emails]
202
+ if good_emails and delete_user_guardrails(good_emails):
203
+ for i in uuid_list:
204
+ deletion_result = client.users.delete_user(i)
205
+ if deletion_result is False:
206
+ click.echo(click.style(f"Error deleting user: {i}", bold=True, fg="red"))
207
+
208
+
209
+ def print_formatted_user_roles(client: Client):
210
+ """Retrieve all available user roles, and format them on screen."""
211
+ unformatted_roles = client.users.describe_available_roles()
212
+ for role_key in unformatted_roles:
213
+ all_values = unformatted_roles[role_key]
214
+ user_id = all_values["id"]
215
+ display_name = all_values["display_name"]
216
+ description = all_values["description"]
217
+ is_global = all_values["is_global"]
218
+ print(click.style(f"ID: {user_id}", bold=True, fg="green"))
219
+ print(f"Display Name: {display_name}")
220
+ print(f"Is Global: {is_global}")
221
+ print("Description:")
222
+ print(description)
223
+ print("")
224
+
225
+
226
+ def add_roles_to_user(client: Client):
227
+ """Autocomplete role names to efficiently add roles to a user via email address.
228
+
229
+ Sensor Download READ permissions required.
230
+ """
231
+ try:
232
+ user_email = prompt("Enter the user email address: ")
233
+ user_uuid = client.users.get_uuid_by_email(user_email)
234
+ except GenericAPIError as error:
235
+ print(click.style(error, bold=True, fg="red"))
236
+ sys.exit()
237
+
238
+ selected_roles = []
239
+ all_roles = []
240
+ all_roles_meta_dict = {}
241
+
242
+ unformatted_roles = client.users.describe_available_roles()
243
+ for role_key in unformatted_roles:
244
+ all_values = unformatted_roles[role_key]
245
+ all_roles.append(all_values["id"])
246
+ all_roles_meta_dict[all_values["id"]] = all_values["display_name"]
247
+
248
+ list_completer = FuzzyWordCompleter(
249
+ all_roles,
250
+ meta_dict=all_roles_meta_dict,
251
+ )
252
+ add_more_roles = "yes"
253
+ while add_more_roles.lower() == "yes" or add_more_roles.lower() == "y":
254
+ role_option = prompt("Select Role: ", completer=list_completer)
255
+ selected_roles.append(role_option)
256
+ add_more_roles = prompt("Add more roles? (yes or no): ")
257
+
258
+ print(f"Adding selected roles: {selected_roles}")
259
+ actually_add = prompt("Continue? (yes or no): ")
260
+ if actually_add.lower() == "yes" or actually_add.lower() == "y":
261
+
262
+ result = client.users.add_user_roles(user_uuid, selected_roles)
263
+ if result is True:
264
+ print(click.style("Successfully added roles", bold=True, fg="green"))
265
+ else:
266
+ print(click.style("Operation Failed, Check Role Name", bold=True, fg="red"))
267
+ else:
268
+ print("Cancelling operation")
269
+
270
+
271
+ def describe_existing_users(client: Client):
272
+ """Show all users in a CID within a formatted table."""
273
+ with click_spinner.spinner():
274
+ user_data = client.users.describe_users()
275
+ role_data = client.users.describe_available_roles()
276
+ formatted_data = list(user_data.values())
277
+
278
+ _tabulate_users(formatted_data, role_data)
@@ -1,11 +1,12 @@
1
1
  [tool.poetry]
2
2
  name = "falcon-toolkit"
3
- version = "3.4.2"
3
+ version = "3.5.0"
4
4
  description = "Toolkit to interface with CrowdStrike Falcon via the API"
5
5
  license = "MIT"
6
6
  authors = [
7
7
  "Chris Hammond <chris.hammond@crowdstrike.com>",
8
8
  "Kira Noël <kira.noel@crowdstrike.com>",
9
+ "Natalie Eckles <natalie.eckles@crowdstrike.com>",
9
10
  ]
10
11
  repository = "http://github.com/CrowdStrike/Falcon-Toolkit"
11
12
  readme = "README.md"
@@ -30,7 +31,7 @@ classifiers = [
30
31
 
31
32
  [tool.poetry.dependencies]
32
33
  python = "^3.9"
33
- caracara = "^0.7.0"
34
+ caracara = "^0.8.0"
34
35
  click = "^8.1.3"
35
36
  click-option-group = "^0.5.6"
36
37
  click-spinner = "^0.1.10"
File without changes