howler-api 2.13.0.dev329__py3-none-any.whl
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.
Potentially problematic release.
This version of howler-api might be problematic. Click here for more details.
- howler/__init__.py +0 -0
- howler/actions/__init__.py +167 -0
- howler/actions/add_label.py +111 -0
- howler/actions/add_to_bundle.py +159 -0
- howler/actions/change_field.py +76 -0
- howler/actions/demote.py +160 -0
- howler/actions/example_plugin.py +104 -0
- howler/actions/prioritization.py +93 -0
- howler/actions/promote.py +147 -0
- howler/actions/remove_from_bundle.py +133 -0
- howler/actions/remove_label.py +111 -0
- howler/actions/transition.py +200 -0
- howler/api/__init__.py +249 -0
- howler/api/base.py +88 -0
- howler/api/socket.py +114 -0
- howler/api/v1/__init__.py +97 -0
- howler/api/v1/action.py +372 -0
- howler/api/v1/analytic.py +748 -0
- howler/api/v1/auth.py +382 -0
- howler/api/v1/borealis.py +101 -0
- howler/api/v1/configs.py +55 -0
- howler/api/v1/dossier.py +222 -0
- howler/api/v1/help.py +28 -0
- howler/api/v1/hit.py +1181 -0
- howler/api/v1/notebook.py +82 -0
- howler/api/v1/overview.py +191 -0
- howler/api/v1/search.py +715 -0
- howler/api/v1/template.py +206 -0
- howler/api/v1/tool.py +183 -0
- howler/api/v1/user.py +414 -0
- howler/api/v1/utils/__init__.py +0 -0
- howler/api/v1/utils/etag.py +84 -0
- howler/api/v1/view.py +288 -0
- howler/app.py +235 -0
- howler/common/README.md +144 -0
- howler/common/__init__.py +0 -0
- howler/common/classification.py +979 -0
- howler/common/classification.yml +107 -0
- howler/common/exceptions.py +167 -0
- howler/common/hexdump.py +48 -0
- howler/common/iprange.py +171 -0
- howler/common/loader.py +154 -0
- howler/common/logging/__init__.py +241 -0
- howler/common/logging/audit.py +138 -0
- howler/common/logging/format.py +38 -0
- howler/common/net.py +79 -0
- howler/common/net_static.py +1494 -0
- howler/common/random_user.py +316 -0
- howler/common/swagger.py +117 -0
- howler/config.py +64 -0
- howler/cronjobs/__init__.py +29 -0
- howler/cronjobs/retention.py +61 -0
- howler/cronjobs/rules.py +274 -0
- howler/cronjobs/view_cleanup.py +88 -0
- howler/datastore/README.md +112 -0
- howler/datastore/__init__.py +0 -0
- howler/datastore/bulk.py +72 -0
- howler/datastore/collection.py +2327 -0
- howler/datastore/constants.py +117 -0
- howler/datastore/exceptions.py +41 -0
- howler/datastore/howler_store.py +105 -0
- howler/datastore/migrations/fix_process.py +41 -0
- howler/datastore/operations.py +130 -0
- howler/datastore/schemas.py +90 -0
- howler/datastore/store.py +231 -0
- howler/datastore/support/__init__.py +0 -0
- howler/datastore/support/build.py +214 -0
- howler/datastore/support/schemas.py +90 -0
- howler/datastore/types.py +22 -0
- howler/error.py +91 -0
- howler/external/__init__.py +0 -0
- howler/external/generate_mitre.py +96 -0
- howler/external/generate_sigma_rules.py +31 -0
- howler/external/generate_tlds.py +47 -0
- howler/external/reindex_data.py +46 -0
- howler/external/wipe_databases.py +58 -0
- howler/gunicorn_config.py +25 -0
- howler/healthz.py +47 -0
- howler/helper/__init__.py +0 -0
- howler/helper/azure.py +50 -0
- howler/helper/discover.py +59 -0
- howler/helper/hit.py +236 -0
- howler/helper/oauth.py +247 -0
- howler/helper/search.py +92 -0
- howler/helper/workflow.py +110 -0
- howler/helper/ws.py +378 -0
- howler/odm/README.md +102 -0
- howler/odm/__init__.py +1 -0
- howler/odm/base.py +1504 -0
- howler/odm/charter.txt +146 -0
- howler/odm/helper.py +416 -0
- howler/odm/howler_enum.py +25 -0
- howler/odm/models/__init__.py +0 -0
- howler/odm/models/action.py +33 -0
- howler/odm/models/analytic.py +90 -0
- howler/odm/models/assemblyline.py +48 -0
- howler/odm/models/aws.py +23 -0
- howler/odm/models/azure.py +16 -0
- howler/odm/models/cbs.py +44 -0
- howler/odm/models/config.py +558 -0
- howler/odm/models/dossier.py +33 -0
- howler/odm/models/ecs/__init__.py +0 -0
- howler/odm/models/ecs/agent.py +17 -0
- howler/odm/models/ecs/autonomous_system.py +16 -0
- howler/odm/models/ecs/client.py +149 -0
- howler/odm/models/ecs/cloud.py +141 -0
- howler/odm/models/ecs/code_signature.py +27 -0
- howler/odm/models/ecs/container.py +32 -0
- howler/odm/models/ecs/dns.py +62 -0
- howler/odm/models/ecs/egress.py +10 -0
- howler/odm/models/ecs/elf.py +74 -0
- howler/odm/models/ecs/email.py +122 -0
- howler/odm/models/ecs/error.py +14 -0
- howler/odm/models/ecs/event.py +140 -0
- howler/odm/models/ecs/faas.py +24 -0
- howler/odm/models/ecs/file.py +84 -0
- howler/odm/models/ecs/geo.py +30 -0
- howler/odm/models/ecs/group.py +18 -0
- howler/odm/models/ecs/hash.py +16 -0
- howler/odm/models/ecs/host.py +17 -0
- howler/odm/models/ecs/http.py +37 -0
- howler/odm/models/ecs/ingress.py +12 -0
- howler/odm/models/ecs/interface.py +21 -0
- howler/odm/models/ecs/network.py +30 -0
- howler/odm/models/ecs/observer.py +45 -0
- howler/odm/models/ecs/organization.py +12 -0
- howler/odm/models/ecs/os.py +21 -0
- howler/odm/models/ecs/pe.py +17 -0
- howler/odm/models/ecs/process.py +216 -0
- howler/odm/models/ecs/registry.py +26 -0
- howler/odm/models/ecs/related.py +45 -0
- howler/odm/models/ecs/rule.py +51 -0
- howler/odm/models/ecs/server.py +24 -0
- howler/odm/models/ecs/threat.py +247 -0
- howler/odm/models/ecs/tls.py +58 -0
- howler/odm/models/ecs/url.py +51 -0
- howler/odm/models/ecs/user.py +57 -0
- howler/odm/models/ecs/user_agent.py +20 -0
- howler/odm/models/ecs/vulnerability.py +41 -0
- howler/odm/models/gcp.py +16 -0
- howler/odm/models/hit.py +356 -0
- howler/odm/models/howler_data.py +328 -0
- howler/odm/models/lead.py +33 -0
- howler/odm/models/localized_label.py +13 -0
- howler/odm/models/overview.py +16 -0
- howler/odm/models/pivot.py +40 -0
- howler/odm/models/template.py +24 -0
- howler/odm/models/user.py +83 -0
- howler/odm/models/view.py +34 -0
- howler/odm/random_data.py +888 -0
- howler/odm/randomizer.py +606 -0
- howler/patched.py +5 -0
- howler/plugins/__init__.py +25 -0
- howler/plugins/config.py +123 -0
- howler/remote/__init__.py +0 -0
- howler/remote/datatypes/README.md +355 -0
- howler/remote/datatypes/__init__.py +98 -0
- howler/remote/datatypes/counters.py +63 -0
- howler/remote/datatypes/events.py +66 -0
- howler/remote/datatypes/hash.py +206 -0
- howler/remote/datatypes/lock.py +42 -0
- howler/remote/datatypes/queues/__init__.py +0 -0
- howler/remote/datatypes/queues/comms.py +59 -0
- howler/remote/datatypes/queues/multi.py +32 -0
- howler/remote/datatypes/queues/named.py +93 -0
- howler/remote/datatypes/queues/priority.py +215 -0
- howler/remote/datatypes/set.py +118 -0
- howler/remote/datatypes/user_quota_tracker.py +54 -0
- howler/security/__init__.py +253 -0
- howler/security/socket.py +108 -0
- howler/security/utils.py +185 -0
- howler/services/__init__.py +0 -0
- howler/services/action_service.py +111 -0
- howler/services/analytic_service.py +128 -0
- howler/services/auth_service.py +323 -0
- howler/services/config_service.py +128 -0
- howler/services/dossier_service.py +252 -0
- howler/services/event_service.py +93 -0
- howler/services/hit_service.py +893 -0
- howler/services/jwt_service.py +158 -0
- howler/services/lucene_service.py +286 -0
- howler/services/notebook_service.py +119 -0
- howler/services/overview_service.py +44 -0
- howler/services/template_service.py +45 -0
- howler/services/user_service.py +330 -0
- howler/utils/__init__.py +0 -0
- howler/utils/annotations.py +28 -0
- howler/utils/chunk.py +38 -0
- howler/utils/dict_utils.py +200 -0
- howler/utils/isotime.py +17 -0
- howler/utils/list_utils.py +11 -0
- howler/utils/lucene.py +77 -0
- howler/utils/path.py +27 -0
- howler/utils/socket_utils.py +61 -0
- howler/utils/str_utils.py +256 -0
- howler/utils/uid.py +47 -0
- howler_api-2.13.0.dev329.dist-info/METADATA +71 -0
- howler_api-2.13.0.dev329.dist-info/RECORD +200 -0
- howler_api-2.13.0.dev329.dist-info/WHEEL +4 -0
- howler_api-2.13.0.dev329.dist-info/entry_points.txt +8 -0
howler/api/v1/dossier.py
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
from flask import request
|
|
2
|
+
|
|
3
|
+
from howler.api import bad_request, created, forbidden, internal_error, make_subapi_blueprint, no_content, not_found, ok
|
|
4
|
+
from howler.common.exceptions import ForbiddenException, HowlerException, InvalidDataException, NotFoundException
|
|
5
|
+
from howler.common.loader import datastore
|
|
6
|
+
from howler.common.logging import get_logger
|
|
7
|
+
from howler.common.swagger import generate_swagger_docs
|
|
8
|
+
from howler.odm.models.dossier import Dossier
|
|
9
|
+
from howler.odm.models.user import User
|
|
10
|
+
from howler.security import api_login
|
|
11
|
+
from howler.services import dossier_service
|
|
12
|
+
|
|
13
|
+
SUB_API = "dossier"
|
|
14
|
+
dossier_api = make_subapi_blueprint(SUB_API, api_version=1)
|
|
15
|
+
dossier_api._doc = "Manage the different dossiers created for filtering hits"
|
|
16
|
+
|
|
17
|
+
logger = get_logger(__file__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@generate_swagger_docs()
|
|
21
|
+
@dossier_api.route("/", methods=["GET"])
|
|
22
|
+
@api_login(required_priv=["R"])
|
|
23
|
+
def get_dossiers(user: User, **kwargs):
|
|
24
|
+
"""Get a list of dossiers the user can see
|
|
25
|
+
|
|
26
|
+
Variables:
|
|
27
|
+
None
|
|
28
|
+
|
|
29
|
+
Optional Arguments:
|
|
30
|
+
None
|
|
31
|
+
|
|
32
|
+
Result Example:
|
|
33
|
+
[
|
|
34
|
+
...dossiers # A list of dossiers the user can use
|
|
35
|
+
]
|
|
36
|
+
"""
|
|
37
|
+
try:
|
|
38
|
+
return ok(
|
|
39
|
+
datastore().dossier.search(
|
|
40
|
+
f"type:global OR owner:({user['uname']} OR none)",
|
|
41
|
+
as_obj=False,
|
|
42
|
+
rows=1000,
|
|
43
|
+
)["items"]
|
|
44
|
+
)
|
|
45
|
+
except ValueError as e:
|
|
46
|
+
return bad_request(err=str(e))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@generate_swagger_docs()
|
|
50
|
+
@dossier_api.route("/", methods=["POST"])
|
|
51
|
+
@api_login(required_priv=["R", "W"])
|
|
52
|
+
def create_dossier(**kwargs):
|
|
53
|
+
"""Create a new dossier
|
|
54
|
+
|
|
55
|
+
Variables:
|
|
56
|
+
None
|
|
57
|
+
|
|
58
|
+
Optional Arguments:
|
|
59
|
+
None
|
|
60
|
+
|
|
61
|
+
Data Block:
|
|
62
|
+
{
|
|
63
|
+
"title": "New dossier" # The name of this dossier
|
|
64
|
+
"query": "howler.id:*" # The query to run
|
|
65
|
+
"type": "global" # The type of dossier - personal or global
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
Result Example:
|
|
69
|
+
{
|
|
70
|
+
...dossier # The new dossier data
|
|
71
|
+
}
|
|
72
|
+
"""
|
|
73
|
+
dossier_data = request.json
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
return created(dossier_service.create_dossier(dossier_data, username=kwargs["user"]["uname"]))
|
|
77
|
+
except InvalidDataException as e:
|
|
78
|
+
return bad_request(err=str(e))
|
|
79
|
+
except HowlerException:
|
|
80
|
+
logger.exception("Exception on create dossier")
|
|
81
|
+
return internal_error(err="An unknown error occured when creating the dossier.")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@generate_swagger_docs()
|
|
85
|
+
@dossier_api.route("/<id>", methods=["GET"])
|
|
86
|
+
@api_login(required_priv=["R"])
|
|
87
|
+
def get_dossier(id: str, user: User, **kwargs):
|
|
88
|
+
"""Get a specific dossier
|
|
89
|
+
|
|
90
|
+
Variables:
|
|
91
|
+
id => The id of the dossier to get
|
|
92
|
+
|
|
93
|
+
Optional Arguments:
|
|
94
|
+
None
|
|
95
|
+
|
|
96
|
+
Result Example:
|
|
97
|
+
[
|
|
98
|
+
...dossiers # A list of dossiers the user can use
|
|
99
|
+
]
|
|
100
|
+
"""
|
|
101
|
+
try:
|
|
102
|
+
results = datastore().dossier.search(
|
|
103
|
+
f"dossier_id:{id}",
|
|
104
|
+
as_obj=False,
|
|
105
|
+
rows=1,
|
|
106
|
+
)["items"]
|
|
107
|
+
|
|
108
|
+
if len(results) < 1:
|
|
109
|
+
return not_found(err="Dossier not found")
|
|
110
|
+
|
|
111
|
+
return ok(results[0])
|
|
112
|
+
except ValueError as e:
|
|
113
|
+
return bad_request(err=str(e))
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@generate_swagger_docs()
|
|
117
|
+
@dossier_api.route("/hit/<id>", methods=["GET"])
|
|
118
|
+
@api_login(required_priv=["R"])
|
|
119
|
+
def get_dossier_for_hit(id: str, user: User, **kwargs):
|
|
120
|
+
"""Get dossiers matching a given hit
|
|
121
|
+
|
|
122
|
+
Variables:
|
|
123
|
+
id => The id of the dossier to get
|
|
124
|
+
|
|
125
|
+
Optional Arguments:
|
|
126
|
+
None
|
|
127
|
+
|
|
128
|
+
Result Example:
|
|
129
|
+
[
|
|
130
|
+
...dossiers # A list of dossiers the user can use
|
|
131
|
+
]
|
|
132
|
+
"""
|
|
133
|
+
storage = datastore()
|
|
134
|
+
try:
|
|
135
|
+
response = storage.hit.search(f"howler.id:{id}", rows=1, as_obj=False)
|
|
136
|
+
|
|
137
|
+
if response["total"] < 1:
|
|
138
|
+
return not_found(err="Hit does not exist.")
|
|
139
|
+
|
|
140
|
+
hit = response["items"][0]
|
|
141
|
+
|
|
142
|
+
return ok(dossier_service.get_matching_dossiers(hit))
|
|
143
|
+
except ValueError as e:
|
|
144
|
+
return bad_request(err=str(e))
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
@generate_swagger_docs()
|
|
148
|
+
@dossier_api.route("/<id>", methods=["DELETE"])
|
|
149
|
+
@api_login(required_priv=["W"])
|
|
150
|
+
def delete_dossier(id: str, user: User, **kwargs):
|
|
151
|
+
"""Delete a dossier
|
|
152
|
+
|
|
153
|
+
Variables:
|
|
154
|
+
id => The id of the dossier to delete
|
|
155
|
+
|
|
156
|
+
Optional Arguments:
|
|
157
|
+
None
|
|
158
|
+
|
|
159
|
+
Data Block:
|
|
160
|
+
None
|
|
161
|
+
|
|
162
|
+
Result Example:
|
|
163
|
+
{
|
|
164
|
+
"success": true # Did the deletion succeed?
|
|
165
|
+
}
|
|
166
|
+
"""
|
|
167
|
+
storage = datastore()
|
|
168
|
+
|
|
169
|
+
existing_dossier: Dossier = storage.dossier.get_if_exists(id)
|
|
170
|
+
if not existing_dossier:
|
|
171
|
+
return not_found(err="This dossier does not exist")
|
|
172
|
+
|
|
173
|
+
if existing_dossier.owner != user.uname and "admin" not in user.type:
|
|
174
|
+
return forbidden(err="You cannot delete a dossier unless you are an administrator, or the owner.")
|
|
175
|
+
|
|
176
|
+
success = storage.dossier.delete(id)
|
|
177
|
+
|
|
178
|
+
storage.dossier.commit()
|
|
179
|
+
|
|
180
|
+
return no_content({"success": success})
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
@generate_swagger_docs()
|
|
184
|
+
@dossier_api.route("/<id>", methods=["PUT"])
|
|
185
|
+
@api_login(required_priv=["R", "W"])
|
|
186
|
+
def update_dossier(id: str, user: User, **kwargs):
|
|
187
|
+
"""Update a dossier
|
|
188
|
+
|
|
189
|
+
Variables:
|
|
190
|
+
id => The id of the dossier to modify
|
|
191
|
+
|
|
192
|
+
Optional Arguments:
|
|
193
|
+
None
|
|
194
|
+
|
|
195
|
+
Data Block:
|
|
196
|
+
{
|
|
197
|
+
"title": "New dossier Name" # The name of this dossier
|
|
198
|
+
"query": "howler.id:*" # The query to run
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
Result Example:
|
|
202
|
+
{
|
|
203
|
+
...dossier # The updated dossier data
|
|
204
|
+
}
|
|
205
|
+
"""
|
|
206
|
+
new_data = request.json
|
|
207
|
+
if not isinstance(new_data, dict):
|
|
208
|
+
return bad_request(err="Invalid data format")
|
|
209
|
+
|
|
210
|
+
try:
|
|
211
|
+
updated_dossier = dossier_service.update_dossier(id, new_data, user)
|
|
212
|
+
|
|
213
|
+
return ok(updated_dossier)
|
|
214
|
+
except ForbiddenException as e:
|
|
215
|
+
return forbidden(err=e.message)
|
|
216
|
+
except InvalidDataException as e:
|
|
217
|
+
return bad_request(err=e.message)
|
|
218
|
+
except NotFoundException as e:
|
|
219
|
+
return not_found(err=e.message)
|
|
220
|
+
except HowlerException as e:
|
|
221
|
+
logger.exception("Unknown error on dossier update:")
|
|
222
|
+
return internal_error(err=e.message)
|
howler/api/v1/help.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from howler.api import make_subapi_blueprint, ok
|
|
2
|
+
from howler.common.swagger import generate_swagger_docs
|
|
3
|
+
from howler.config import CLASSIFICATION
|
|
4
|
+
from howler.security import api_login
|
|
5
|
+
|
|
6
|
+
SUB_API = "help"
|
|
7
|
+
classification_definition = CLASSIFICATION.get_parsed_classification_definition()
|
|
8
|
+
|
|
9
|
+
help_api = make_subapi_blueprint(SUB_API, api_version=1)
|
|
10
|
+
help_api._doc = "Provide information about the system configuration"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@generate_swagger_docs()
|
|
14
|
+
@help_api.route("/classification_definition")
|
|
15
|
+
@api_login(audit=False, check_xsrf_token=False)
|
|
16
|
+
def get_classification_definition(**_):
|
|
17
|
+
"""Return the current system classification definition
|
|
18
|
+
|
|
19
|
+
Variables:
|
|
20
|
+
None
|
|
21
|
+
|
|
22
|
+
Arguments:
|
|
23
|
+
None
|
|
24
|
+
|
|
25
|
+
Result Example:
|
|
26
|
+
A parsed classification definition. (This is more for internal use)
|
|
27
|
+
"""
|
|
28
|
+
return ok(classification_definition)
|