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.
- 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
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
from flask import request
|
|
2
|
+
|
|
3
|
+
from howler.api import bad_request, internal_error, make_subapi_blueprint, ok
|
|
4
|
+
from howler.common.swagger import generate_swagger_docs
|
|
5
|
+
from howler.security import api_login
|
|
6
|
+
from howler.services import notebook_service
|
|
7
|
+
|
|
8
|
+
SUB_API = "notebook"
|
|
9
|
+
notebook_api = make_subapi_blueprint(SUB_API, api_version=1)
|
|
10
|
+
notebook_api._doc = "Get notebook information"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@generate_swagger_docs()
|
|
14
|
+
@notebook_api.route("/environments", methods=["GET"])
|
|
15
|
+
@api_login(required_priv=["R"], required_method=["oauth"])
|
|
16
|
+
def get_user_environments(**kwargs):
|
|
17
|
+
"""Get user's jupyter hub environments
|
|
18
|
+
|
|
19
|
+
Variables:
|
|
20
|
+
None
|
|
21
|
+
|
|
22
|
+
Arguments:
|
|
23
|
+
|
|
24
|
+
Result Example:
|
|
25
|
+
{
|
|
26
|
+
[
|
|
27
|
+
Env1,
|
|
28
|
+
Env2
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
"""
|
|
32
|
+
try:
|
|
33
|
+
env = notebook_service.get_user_envs()
|
|
34
|
+
|
|
35
|
+
return ok({"envs": env})
|
|
36
|
+
except Exception as error:
|
|
37
|
+
return internal_error(err=f"Failed to retrieve user's environments from nbgallery. {error}")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@generate_swagger_docs()
|
|
41
|
+
@notebook_api.route("/notebook", methods=["POST"])
|
|
42
|
+
@api_login(required_priv=["R"], required_method=["oauth"])
|
|
43
|
+
def get_notebook(**kwargs):
|
|
44
|
+
"""Return patched notebook
|
|
45
|
+
|
|
46
|
+
Variables:
|
|
47
|
+
None
|
|
48
|
+
|
|
49
|
+
Arguments:
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
Data Block:
|
|
53
|
+
{
|
|
54
|
+
link: "https://nbgallery...",
|
|
55
|
+
analytic: Analytic object,
|
|
56
|
+
hit: Hit object
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
Result Example:
|
|
60
|
+
{
|
|
61
|
+
[
|
|
62
|
+
Env1,
|
|
63
|
+
Env2
|
|
64
|
+
]
|
|
65
|
+
}
|
|
66
|
+
"""
|
|
67
|
+
data = request.json
|
|
68
|
+
if not isinstance(data, dict):
|
|
69
|
+
return bad_request(err="Invalid data format")
|
|
70
|
+
|
|
71
|
+
if "link" not in data:
|
|
72
|
+
return bad_request(err="You must provide a link")
|
|
73
|
+
|
|
74
|
+
if "analytic" not in data:
|
|
75
|
+
return bad_request(err="You must provide an analytic")
|
|
76
|
+
|
|
77
|
+
try:
|
|
78
|
+
json_content, name = notebook_service.get_nb_information(data["link"], data["analytic"], data.get("hit", {}))
|
|
79
|
+
except Exception as error:
|
|
80
|
+
return internal_error(err=f"Failed to retrieve notebook from nbgallery. {error}")
|
|
81
|
+
|
|
82
|
+
return ok({"nb_content": json_content, "name": name})
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
from flask import request
|
|
2
|
+
|
|
3
|
+
from howler.api import (
|
|
4
|
+
bad_request,
|
|
5
|
+
conflict,
|
|
6
|
+
created,
|
|
7
|
+
forbidden,
|
|
8
|
+
make_subapi_blueprint,
|
|
9
|
+
no_content,
|
|
10
|
+
not_found,
|
|
11
|
+
ok,
|
|
12
|
+
)
|
|
13
|
+
from howler.common.exceptions import HowlerException
|
|
14
|
+
from howler.common.loader import datastore
|
|
15
|
+
from howler.common.logging import get_logger
|
|
16
|
+
from howler.common.swagger import generate_swagger_docs
|
|
17
|
+
from howler.odm.models.overview import Overview
|
|
18
|
+
from howler.odm.models.user import User
|
|
19
|
+
from howler.security import api_login
|
|
20
|
+
from howler.utils.str_utils import sanitize_lucene_query
|
|
21
|
+
|
|
22
|
+
SUB_API = "overview"
|
|
23
|
+
overview_api = make_subapi_blueprint(SUB_API, api_version=1)
|
|
24
|
+
overview_api._doc = "Manage the different overviews created for viewing hits"
|
|
25
|
+
|
|
26
|
+
logger = get_logger(__file__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@generate_swagger_docs()
|
|
30
|
+
@overview_api.route("/", methods=["GET"])
|
|
31
|
+
@api_login(required_priv=["R"])
|
|
32
|
+
def get_overviews(**kwargs):
|
|
33
|
+
"""Get a list of overviews the user can use to render hits
|
|
34
|
+
|
|
35
|
+
Variables:
|
|
36
|
+
None
|
|
37
|
+
|
|
38
|
+
Optional Arguments:
|
|
39
|
+
None
|
|
40
|
+
|
|
41
|
+
Result Example:
|
|
42
|
+
[
|
|
43
|
+
...overviews # A list of overviews the user can use
|
|
44
|
+
]
|
|
45
|
+
"""
|
|
46
|
+
try:
|
|
47
|
+
return ok(
|
|
48
|
+
datastore().overview.search(
|
|
49
|
+
"overview_id:*",
|
|
50
|
+
as_obj=False,
|
|
51
|
+
rows=10000,
|
|
52
|
+
)["items"]
|
|
53
|
+
)
|
|
54
|
+
except ValueError as e:
|
|
55
|
+
return bad_request(err=str(e))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@generate_swagger_docs()
|
|
59
|
+
@overview_api.route("/", methods=["POST"])
|
|
60
|
+
@api_login(required_priv=["R", "W"])
|
|
61
|
+
def create_overview(**kwargs):
|
|
62
|
+
"""Create a new overview
|
|
63
|
+
|
|
64
|
+
Variables:
|
|
65
|
+
None
|
|
66
|
+
|
|
67
|
+
Optional Arguments:
|
|
68
|
+
None
|
|
69
|
+
|
|
70
|
+
Data Block:
|
|
71
|
+
{
|
|
72
|
+
"analytic": "analytic name" # The analytic this overview applies to
|
|
73
|
+
"detection": "detection name" # The detection this overview applies to
|
|
74
|
+
"content": "# Hello, World!\n\nExample Text" # The content to show when this overview matches a hit
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
Result Example:
|
|
78
|
+
{
|
|
79
|
+
...overview # The new overview data
|
|
80
|
+
}
|
|
81
|
+
"""
|
|
82
|
+
overview_data = request.json
|
|
83
|
+
if not isinstance(overview_data, dict):
|
|
84
|
+
return bad_request(err="Invalid data format")
|
|
85
|
+
|
|
86
|
+
if "content" not in overview_data:
|
|
87
|
+
return bad_request(err="You must specify content when creating an overview!")
|
|
88
|
+
|
|
89
|
+
storage = datastore()
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
overview = Overview(overview_data)
|
|
93
|
+
|
|
94
|
+
overview.owner = kwargs["user"]["uname"]
|
|
95
|
+
|
|
96
|
+
query_str = f"analytic:{sanitize_lucene_query(overview.analytic)}"
|
|
97
|
+
|
|
98
|
+
if overview.detection:
|
|
99
|
+
query_str += f" AND detection:{overview.detection}"
|
|
100
|
+
else:
|
|
101
|
+
query_str += " AND -_exists_:detection"
|
|
102
|
+
|
|
103
|
+
if storage.overview.search(query_str)["total"] > 0:
|
|
104
|
+
return conflict(err="An overview covering this case already exists.")
|
|
105
|
+
|
|
106
|
+
storage.overview.save(overview.overview_id, overview)
|
|
107
|
+
return created(overview)
|
|
108
|
+
except HowlerException as e:
|
|
109
|
+
return bad_request(err=str(e))
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@generate_swagger_docs()
|
|
113
|
+
@overview_api.route("/<id>", methods=["DELETE"])
|
|
114
|
+
@api_login(required_priv=["W"])
|
|
115
|
+
def delete_overview(id: str, user: User, **kwargs):
|
|
116
|
+
"""Delete an overview
|
|
117
|
+
|
|
118
|
+
Variables:
|
|
119
|
+
id => The id of the overview to delete
|
|
120
|
+
|
|
121
|
+
Optional Arguments:
|
|
122
|
+
None
|
|
123
|
+
|
|
124
|
+
Data Block:
|
|
125
|
+
None
|
|
126
|
+
|
|
127
|
+
Result Example:
|
|
128
|
+
{
|
|
129
|
+
"success": true # Did the deletion succeed?
|
|
130
|
+
}
|
|
131
|
+
"""
|
|
132
|
+
storage = datastore()
|
|
133
|
+
|
|
134
|
+
if not storage.overview.exists(id):
|
|
135
|
+
return not_found(err="This overview does not exist")
|
|
136
|
+
|
|
137
|
+
existing_overview: Overview = storage.overview.get_if_exists(id)
|
|
138
|
+
|
|
139
|
+
if existing_overview.owner != user.uname and "admin" not in user.type:
|
|
140
|
+
return forbidden(err="You cannot delete an overview that is not owned by you.")
|
|
141
|
+
|
|
142
|
+
result = storage.overview.delete(id)
|
|
143
|
+
if result:
|
|
144
|
+
return no_content()
|
|
145
|
+
else:
|
|
146
|
+
return not_found()
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@generate_swagger_docs()
|
|
150
|
+
@overview_api.route("/<id>", methods=["PUT"])
|
|
151
|
+
@api_login(required_priv=["R", "W"])
|
|
152
|
+
def update_overview_content(id: str, user: User, **kwargs):
|
|
153
|
+
"""Update an overview's content
|
|
154
|
+
|
|
155
|
+
Variables:
|
|
156
|
+
id => The id of the overview to modify
|
|
157
|
+
|
|
158
|
+
Optional Arguments:
|
|
159
|
+
None
|
|
160
|
+
|
|
161
|
+
Data Block:
|
|
162
|
+
{
|
|
163
|
+
"content: "# New Markdown\n\nExample
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
Result Example:
|
|
167
|
+
{
|
|
168
|
+
...overview # The updated overview data
|
|
169
|
+
}
|
|
170
|
+
"""
|
|
171
|
+
storage = datastore()
|
|
172
|
+
|
|
173
|
+
if not storage.overview.exists(id):
|
|
174
|
+
return not_found(err="This overview does not exist")
|
|
175
|
+
|
|
176
|
+
data = request.json
|
|
177
|
+
if not data or not isinstance(data.get("content", None), str):
|
|
178
|
+
return bad_request(err="New overview content must be a string.")
|
|
179
|
+
|
|
180
|
+
content = data["content"]
|
|
181
|
+
|
|
182
|
+
existing_overview: Overview = storage.overview.get_if_exists(id)
|
|
183
|
+
|
|
184
|
+
existing_overview.content = content
|
|
185
|
+
|
|
186
|
+
storage.overview.save(existing_overview.overview_id, existing_overview)
|
|
187
|
+
|
|
188
|
+
try:
|
|
189
|
+
return ok(storage.overview.get_if_exists(existing_overview.overview_id, as_obj=False))
|
|
190
|
+
except HowlerException as e:
|
|
191
|
+
return bad_request(err=str(e))
|