howler-api 2.13.0.dev341__py3-none-any.whl → 2.13.0.dev343__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/api/__init__.py +1 -1
- howler/api/v1/{borealis.py → clue.py} +24 -26
- howler/api/v1/search.py +74 -1
- howler/app.py +4 -4
- howler/odm/helper.py +5 -5
- howler/odm/models/config.py +10 -10
- howler/odm/models/pivot.py +1 -1
- howler/security/__init__.py +2 -2
- howler/services/config_service.py +2 -2
- {howler_api-2.13.0.dev341.dist-info → howler_api-2.13.0.dev343.dist-info}/METADATA +1 -1
- {howler_api-2.13.0.dev341.dist-info → howler_api-2.13.0.dev343.dist-info}/RECORD +13 -13
- {howler_api-2.13.0.dev341.dist-info → howler_api-2.13.0.dev343.dist-info}/WHEEL +0 -0
- {howler_api-2.13.0.dev341.dist-info → howler_api-2.13.0.dev343.dist-info}/entry_points.txt +0 -0
howler/api/__init__.py
CHANGED
|
@@ -32,7 +32,7 @@ def _make_api_response(
|
|
|
32
32
|
) -> Response:
|
|
33
33
|
quota_user = flsk_session.pop("quota_user", None)
|
|
34
34
|
quota_set = flsk_session.pop("quota_set", False)
|
|
35
|
-
if quota_user and quota_set and not request.path.startswith("/api/v1/
|
|
35
|
+
if quota_user and quota_set and not request.path.startswith("/api/v1/clue"):
|
|
36
36
|
QUOTA_TRACKER.end(quota_user)
|
|
37
37
|
|
|
38
38
|
if type(err) is Exception: # pragma: no cover
|
|
@@ -14,9 +14,9 @@ from howler.config import cache, config
|
|
|
14
14
|
from howler.plugins import get_plugins
|
|
15
15
|
from howler.security import api_login
|
|
16
16
|
|
|
17
|
-
SUB_API = "
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
SUB_API = "clue"
|
|
18
|
+
clue_api = make_subapi_blueprint(SUB_API, api_version=1)
|
|
19
|
+
clue_api._doc = "Proxy enrichment requests to clue"
|
|
20
20
|
|
|
21
21
|
logger = get_logger(__file__)
|
|
22
22
|
|
|
@@ -28,27 +28,27 @@ def skip_cache(*args):
|
|
|
28
28
|
|
|
29
29
|
@cache.memoize(15 * 60, unless=skip_cache)
|
|
30
30
|
def get_token(access_token: str) -> str:
|
|
31
|
-
"""Get a
|
|
32
|
-
|
|
31
|
+
"""Get a clue token based on the current howler token"""
|
|
32
|
+
get_clue_token: Optional[Callable[[str], str]] = None
|
|
33
33
|
|
|
34
34
|
for plugin in get_plugins():
|
|
35
|
-
if
|
|
35
|
+
if get_clue_token := plugin.modules.token_functions.get("clue", None):
|
|
36
36
|
break
|
|
37
37
|
|
|
38
|
-
if
|
|
39
|
-
|
|
38
|
+
if get_clue_token:
|
|
39
|
+
clue_access_token = get_clue_token(access_token)
|
|
40
40
|
else:
|
|
41
|
-
logger.info("No custom
|
|
42
|
-
|
|
41
|
+
logger.info("No custom clue token logic provided, continuing with howler credentials")
|
|
42
|
+
clue_access_token = access_token
|
|
43
43
|
|
|
44
|
-
return
|
|
44
|
+
return clue_access_token
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
@generate_swagger_docs()
|
|
48
|
-
@
|
|
48
|
+
@clue_api.route("/<path:path>", methods=["GET", "POST"])
|
|
49
49
|
@api_login(required_priv=["R"], required_method=["oauth"])
|
|
50
|
-
def
|
|
51
|
-
"""Proxy enrichment requests to
|
|
50
|
+
def proxy_to_clue(path, **kwargs):
|
|
51
|
+
"""Proxy enrichment requests to Clue
|
|
52
52
|
|
|
53
53
|
Variables:
|
|
54
54
|
None
|
|
@@ -60,11 +60,9 @@ def proxy_to_borealis(path, **kwargs):
|
|
|
60
60
|
Any
|
|
61
61
|
|
|
62
62
|
Result Example:
|
|
63
|
-
|
|
63
|
+
Clue Responses
|
|
64
64
|
"""
|
|
65
|
-
logger.info(
|
|
66
|
-
"Proxying borealis request to path %s/%s?%s", config.core.borealis.url, path, request.query_string.decode()
|
|
67
|
-
)
|
|
65
|
+
logger.info("Proxying clue request to path %s/%s?%s", config.core.clue.url, path, request.query_string.decode())
|
|
68
66
|
|
|
69
67
|
auth_data: Optional[str] = request.headers.get("Authorization", None, type=str)
|
|
70
68
|
|
|
@@ -73,29 +71,29 @@ def proxy_to_borealis(path, **kwargs):
|
|
|
73
71
|
|
|
74
72
|
auth_token = auth_data.split(" ")[1]
|
|
75
73
|
|
|
76
|
-
|
|
74
|
+
clue_token = get_token(auth_token)
|
|
77
75
|
|
|
78
76
|
start = time.perf_counter()
|
|
79
|
-
with elasticapm.capture_span("
|
|
77
|
+
with elasticapm.capture_span("clue", span_type="http"):
|
|
80
78
|
if request.method.lower() == "get":
|
|
81
79
|
response = requests.get(
|
|
82
|
-
f"{config.core.
|
|
83
|
-
headers={"Authorization": f"Bearer {
|
|
80
|
+
f"{config.core.clue.url}/{path}",
|
|
81
|
+
headers={"Authorization": f"Bearer {clue_token}", "Accept": "application/json"},
|
|
84
82
|
params=request.args.to_dict(),
|
|
85
83
|
timeout=5 * 60,
|
|
86
84
|
)
|
|
87
85
|
else:
|
|
88
86
|
response = requests.post(
|
|
89
|
-
f"{config.core.
|
|
87
|
+
f"{config.core.clue.url}/{path}",
|
|
90
88
|
json=request.json,
|
|
91
|
-
headers={"Authorization": f"Bearer {
|
|
89
|
+
headers={"Authorization": f"Bearer {clue_token}", "Accept": "application/json"},
|
|
92
90
|
params=request.args.to_dict(),
|
|
93
91
|
timeout=5 * 60,
|
|
94
92
|
)
|
|
95
93
|
|
|
96
|
-
logger.debug(f"Request to
|
|
94
|
+
logger.debug(f"Request to clue completed in {round(time.perf_counter() - start)}ms")
|
|
97
95
|
|
|
98
96
|
if not response.ok:
|
|
99
|
-
return bad_gateway(response.json(), err="Something went wrong when connecting to
|
|
97
|
+
return bad_gateway(response.json(), err="Something went wrong when connecting to clue")
|
|
100
98
|
|
|
101
99
|
return ok(response.json()["api_response"])
|
howler/api/v1/search.py
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from copy import deepcopy
|
|
1
3
|
from typing import Any, Union
|
|
2
4
|
|
|
3
5
|
from elasticsearch import BadRequestError
|
|
6
|
+
from elasticsearch._sync.client.indices import IndicesClient
|
|
4
7
|
from flask import request
|
|
5
8
|
from sigma.backends.elasticsearch import LuceneBackend
|
|
6
9
|
from sigma.rule import SigmaRule
|
|
@@ -14,7 +17,7 @@ from howler.common.swagger import generate_swagger_docs
|
|
|
14
17
|
from howler.datastore.exceptions import SearchException
|
|
15
18
|
from howler.helper.search import get_collection, get_default_sort, has_access_control, list_all_fields
|
|
16
19
|
from howler.security import api_login
|
|
17
|
-
from howler.services import hit_service
|
|
20
|
+
from howler.services import hit_service, lucene_service
|
|
18
21
|
|
|
19
22
|
SUB_API = "search"
|
|
20
23
|
search_api = make_subapi_blueprint(SUB_API, api_version=1)
|
|
@@ -146,6 +149,76 @@ def search(index, **kwargs):
|
|
|
146
149
|
return bad_request(err=f"SearchException: {e}")
|
|
147
150
|
|
|
148
151
|
|
|
152
|
+
@generate_swagger_docs()
|
|
153
|
+
@search_api.route("/<index>/explain", methods=["GET", "POST"])
|
|
154
|
+
@api_login(required_priv=["R"])
|
|
155
|
+
def explain_query(index, **kwargs):
|
|
156
|
+
"""Search through specified index for a given Lucene query. Uses Lucene search syntax for query.
|
|
157
|
+
|
|
158
|
+
Variables:
|
|
159
|
+
index => Index to explain against (hit, user,...)
|
|
160
|
+
|
|
161
|
+
Arguments:
|
|
162
|
+
query => Lucene Query to explain
|
|
163
|
+
|
|
164
|
+
Data Block:
|
|
165
|
+
# Note that the data block is for POST requests only!
|
|
166
|
+
{
|
|
167
|
+
"query": "id:*", # Lucene Query to explain
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
Result Example:
|
|
172
|
+
{
|
|
173
|
+
'valid': True,
|
|
174
|
+
'explanations': [
|
|
175
|
+
{
|
|
176
|
+
'valid': True,
|
|
177
|
+
'explanation': 'ConstantScore(FieldExistsQuery [field=id])'
|
|
178
|
+
}
|
|
179
|
+
]
|
|
180
|
+
}
|
|
181
|
+
"""
|
|
182
|
+
user = kwargs["user"]
|
|
183
|
+
collection = get_collection(index, user)
|
|
184
|
+
|
|
185
|
+
if collection is None:
|
|
186
|
+
return bad_request(err=f"Not a valid index to explain: {index}")
|
|
187
|
+
|
|
188
|
+
fields = ["query"]
|
|
189
|
+
multi_fields: list[str] = []
|
|
190
|
+
|
|
191
|
+
params, req_data = generate_params(request, fields, multi_fields)
|
|
192
|
+
|
|
193
|
+
params["as_obj"] = False
|
|
194
|
+
|
|
195
|
+
query = req_data.get("query", None)
|
|
196
|
+
if not query:
|
|
197
|
+
return bad_request(err="There was no query.")
|
|
198
|
+
|
|
199
|
+
# This regex checks for lucene phrases (i.e. the "Example Analytic" part of howler.analytic:"Example Analytic")
|
|
200
|
+
# And then escapes them.
|
|
201
|
+
# https://regex101.com/r/8u5F6a/1
|
|
202
|
+
escaped_lucene = re.sub(r'((:\()?(".+?")(\)?))', lucene_service.replace_lucene_phrase, query)
|
|
203
|
+
|
|
204
|
+
try:
|
|
205
|
+
indices_client = IndicesClient(datastore().hit.datastore.client)
|
|
206
|
+
|
|
207
|
+
result = deepcopy(
|
|
208
|
+
indices_client.validate_query(q=escaped_lucene, explain=True, index=collection().index_name).body
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
del result["_shards"]
|
|
212
|
+
|
|
213
|
+
for explanation in result["explanations"]:
|
|
214
|
+
del explanation["index"]
|
|
215
|
+
|
|
216
|
+
return ok(result)
|
|
217
|
+
except Exception as e:
|
|
218
|
+
logger.exception("Exception on query explanation")
|
|
219
|
+
return bad_request(err=f"Exception: {e}")
|
|
220
|
+
|
|
221
|
+
|
|
149
222
|
@generate_swagger_docs()
|
|
150
223
|
@search_api.route("/<index>/eql", methods=["GET", "POST"])
|
|
151
224
|
@api_login(required_priv=["R"])
|
howler/app.py
CHANGED
|
@@ -138,11 +138,11 @@ if HWL_USE_REST_API or DEBUG:
|
|
|
138
138
|
logger.debug("Enabled Notebook Integration")
|
|
139
139
|
app.register_blueprint(notebook_api)
|
|
140
140
|
|
|
141
|
-
if config.core.
|
|
142
|
-
from howler.api.v1.
|
|
141
|
+
if config.core.clue.enabled:
|
|
142
|
+
from howler.api.v1.clue import clue_api
|
|
143
143
|
|
|
144
|
-
logger.debug("Enabled
|
|
145
|
-
app.register_blueprint(
|
|
144
|
+
logger.debug("Enabled Clue Integration")
|
|
145
|
+
app.register_blueprint(clue_api)
|
|
146
146
|
|
|
147
147
|
logger.info("Checking plugins for additional routes")
|
|
148
148
|
for plugin in get_plugins():
|
howler/odm/helper.py
CHANGED
|
@@ -259,13 +259,13 @@ def generate_useful_hit(lookups: dict[str, dict[str, Any]], users: list[User], p
|
|
|
259
259
|
),
|
|
260
260
|
]
|
|
261
261
|
|
|
262
|
-
if config.core.
|
|
262
|
+
if config.core.clue.enabled:
|
|
263
263
|
hit.howler.dossier.append(
|
|
264
264
|
Lead(
|
|
265
265
|
{
|
|
266
266
|
"icon": "material-symbols:image",
|
|
267
|
-
"label": {"en": "
|
|
268
|
-
"format": "
|
|
267
|
+
"label": {"en": "Clue", "fr": "Clue"},
|
|
268
|
+
"format": "clue",
|
|
269
269
|
"content": "test-plugin.image",
|
|
270
270
|
"metadata": {"type": "ip", "value": "127.0.01", "classification": "TLP:CLEAR"},
|
|
271
271
|
}
|
|
@@ -276,8 +276,8 @@ def generate_useful_hit(lookups: dict[str, dict[str, Any]], users: list[User], p
|
|
|
276
276
|
Lead(
|
|
277
277
|
{
|
|
278
278
|
"icon": "material-symbols:code-rounded",
|
|
279
|
-
"label": {"en": "
|
|
280
|
-
"format": "
|
|
279
|
+
"label": {"en": "Clue", "fr": "Clue"},
|
|
280
|
+
"format": "clue",
|
|
281
281
|
"content": "test-plugin.json",
|
|
282
282
|
"metadata": {"type": "ip", "value": "127.0.01", "classification": "TLP:CLEAR"},
|
|
283
283
|
}
|
howler/odm/models/config.py
CHANGED
|
@@ -409,24 +409,24 @@ class UI(BaseModel):
|
|
|
409
409
|
)
|
|
410
410
|
|
|
411
411
|
|
|
412
|
-
class
|
|
413
|
-
"""
|
|
412
|
+
class Clue(BaseModel):
|
|
413
|
+
"""Clue enrichment service integration configuration.
|
|
414
414
|
|
|
415
|
-
Defines settings for integrating with
|
|
415
|
+
Defines settings for integrating with Clue, an external enrichment
|
|
416
416
|
service that can provide additional context and status information for
|
|
417
417
|
hits displayed in the Howler UI.
|
|
418
418
|
"""
|
|
419
419
|
|
|
420
|
-
enabled: bool = Field(default=False, description="Should
|
|
420
|
+
enabled: bool = Field(default=False, description="Should clue integration be enabled?")
|
|
421
421
|
|
|
422
422
|
url: str = Field(
|
|
423
423
|
default="http://enrichment-rest.enrichment.svc.cluster.local:5000",
|
|
424
|
-
description="What url should Howler connect to to interact with
|
|
424
|
+
description="What url should Howler connect to to interact with Clue?",
|
|
425
425
|
)
|
|
426
426
|
|
|
427
427
|
status_checks: list[str] = Field(
|
|
428
428
|
default=[],
|
|
429
|
-
description="A list of
|
|
429
|
+
description="A list of clue fetchers that return status results given a Howler ID to show in the UI.",
|
|
430
430
|
)
|
|
431
431
|
|
|
432
432
|
|
|
@@ -451,7 +451,7 @@ class Core(BaseModel):
|
|
|
451
451
|
"""Core application configuration for Howler.
|
|
452
452
|
|
|
453
453
|
Aggregates all core service configurations including Redis, metrics,
|
|
454
|
-
and external integrations like
|
|
454
|
+
and external integrations like Clue and nbgallery notebooks.
|
|
455
455
|
Also manages the loading of external plugins.
|
|
456
456
|
"""
|
|
457
457
|
|
|
@@ -463,8 +463,8 @@ class Core(BaseModel):
|
|
|
463
463
|
redis: Redis = Redis()
|
|
464
464
|
"Configuration for Redis instances"
|
|
465
465
|
|
|
466
|
-
|
|
467
|
-
"Configuration for
|
|
466
|
+
clue: Clue = Clue()
|
|
467
|
+
"Configuration for Clue Integration"
|
|
468
468
|
|
|
469
469
|
notebook: Notebook = Notebook()
|
|
470
470
|
"Configuration for Notebook Integration"
|
|
@@ -529,7 +529,7 @@ class Config(BaseSettings):
|
|
|
529
529
|
logging: Logging = Logging()
|
|
530
530
|
system: System = System()
|
|
531
531
|
ui: UI = UI()
|
|
532
|
-
mapping: dict[str, str] = Field(description="Mapping of alert keys to
|
|
532
|
+
mapping: dict[str, str] = Field(description="Mapping of alert keys to clue types", default={})
|
|
533
533
|
|
|
534
534
|
model_config = SettingsConfigDict(
|
|
535
535
|
yaml_file=config_locations,
|
howler/odm/models/pivot.py
CHANGED
|
@@ -31,7 +31,7 @@ class Pivot(odm.Model):
|
|
|
31
31
|
description="An optional icon to use in the tab display for this dossier.", optional=True
|
|
32
32
|
)
|
|
33
33
|
label: LocalizedLabel = odm.Compound(LocalizedLabel, description="Labels for the pivot in the UI.")
|
|
34
|
-
value: str = odm.Keyword(description="The link/
|
|
34
|
+
value: str = odm.Keyword(description="The link/plugin information to pivot on.")
|
|
35
35
|
format: str = odm.Keyword(description="The format of the pivot.")
|
|
36
36
|
mappings: list[Mapping] = odm.List(
|
|
37
37
|
odm.Compound(Mapping),
|
howler/security/__init__.py
CHANGED
|
@@ -220,8 +220,8 @@ class api_login(object): # noqa: D101, N801
|
|
|
220
220
|
user_id=user.get("uname", None),
|
|
221
221
|
)
|
|
222
222
|
|
|
223
|
-
if request.path.startswith("/api/v1/
|
|
224
|
-
logger.debug("Bypassing quota limits for
|
|
223
|
+
if request.path.startswith("/api/v1/clue"):
|
|
224
|
+
logger.debug("Bypassing quota limits for clue enrichment")
|
|
225
225
|
elif self.enforce_quota:
|
|
226
226
|
# Check current user quota
|
|
227
227
|
flsk_session["quota_user"] = user["uname"]
|
|
@@ -117,11 +117,11 @@ def get_configuration(user: User, **kwargs):
|
|
|
117
117
|
},
|
|
118
118
|
"mapping": config.mapping,
|
|
119
119
|
"features": {
|
|
120
|
-
"
|
|
120
|
+
"clue": config.core.clue.enabled,
|
|
121
121
|
"notebook": config.core.notebook.enabled,
|
|
122
122
|
**plugin_features,
|
|
123
123
|
},
|
|
124
|
-
"
|
|
124
|
+
"clue": {"status_checks": config.core.clue.status_checks},
|
|
125
125
|
},
|
|
126
126
|
"c12nDef": classification_definition,
|
|
127
127
|
"indexes": list_all_fields("admin" in user["type"] if user is not None else False),
|
|
@@ -10,28 +10,28 @@ howler/actions/promote.py,sha256=v2lFS4WkOpX47ChbSYbhJKzwAShYE3Fg2UVQ6sbr7Yw,470
|
|
|
10
10
|
howler/actions/remove_from_bundle.py,sha256=Tt9zPZuPrplM8lvCo2sHKORY4U4z3S6RfltXRigDvF4,4251
|
|
11
11
|
howler/actions/remove_label.py,sha256=I6_4KnBgvW6OwP7tNe5LMbaOZfAVf5S9ADijBt7g-9A,3325
|
|
12
12
|
howler/actions/transition.py,sha256=7gGWGY0wKZjp87-BpqfMjImAz-QXHSyDJA3bWq3RTz4,6423
|
|
13
|
-
howler/api/__init__.py,sha256=
|
|
13
|
+
howler/api/__init__.py,sha256=Z-o_fukNO9wBpx9gLNm8OPZwFOP-4xMtY0V1sfdEn8w,8539
|
|
14
14
|
howler/api/base.py,sha256=i--N7f4itJBRNVayDAqOiZYIq9thXwdblRBGfePyP1c,2689
|
|
15
15
|
howler/api/socket.py,sha256=6T_gpyazIbSmA2BheZ-yUWPbdxg9C50az70-rq4o-Rc,3910
|
|
16
16
|
howler/api/v1/__init__.py,sha256=lupGaCMXiiVWJjWrV7AoutAcTvZUrYommD-oUf4SFgU,3857
|
|
17
17
|
howler/api/v1/action.py,sha256=sZFihfJQFmzhg9Gc7o0A73pDZls_s8Xjf3mfzB2Nkr4,11112
|
|
18
18
|
howler/api/v1/analytic.py,sha256=kN7YXSTaXEWusu5DjAC510qlDzAIzsV7j7QjNw1daoY,20146
|
|
19
19
|
howler/api/v1/auth.py,sha256=fj0t6COXesgrShZQlP0zZXR12htjYlzLGGMIDsiX8nM,14069
|
|
20
|
-
howler/api/v1/
|
|
20
|
+
howler/api/v1/clue.py,sha256=QaXmk9hTIkt7Cp4oAjCZuhtGeOo_C6yeNmhQY-egVsM,3042
|
|
21
21
|
howler/api/v1/configs.py,sha256=cjii7WY1AtTyk56O84_WR-_JVonoXpWCrh2eMEuuGro,1921
|
|
22
22
|
howler/api/v1/dossier.py,sha256=8mfuoxAHqT9a_0BHRBO9G33KLJUa9xwcfOImb6ax8mU,5816
|
|
23
23
|
howler/api/v1/help.py,sha256=hqBvzW-DYX-I4Q85dbCQgpHsXyWDXp06a1fE9zhFMH4,823
|
|
24
24
|
howler/api/v1/hit.py,sha256=HKaI72k7VC0v4QPjE2zcAV6jizvY2ZWEpk1BiAklvgU,34031
|
|
25
25
|
howler/api/v1/notebook.py,sha256=-cahdJ9u1lAB_nheztayHKtXvoPZcj04N8nqQ2U-HLo,2015
|
|
26
26
|
howler/api/v1/overview.py,sha256=e-WiHuMvOPy8Y7xIGnceagdI0_jebqlgDxi6GE6tfaA,4999
|
|
27
|
-
howler/api/v1/search.py,sha256=
|
|
27
|
+
howler/api/v1/search.py,sha256=24r1vaYaxDhddzDR-c5dBdRKyag6aIggvHSWPWudqN8,26372
|
|
28
28
|
howler/api/v1/template.py,sha256=oexzUpAMVdgI6IOXITPx0iEfZIVP-kvlExRdFd6QVdk,5769
|
|
29
29
|
howler/api/v1/tool.py,sha256=6CN5LIXgxaAvxEHE6s4IFtAEhQo_zBLtl6ghbM8whjE,6737
|
|
30
30
|
howler/api/v1/user.py,sha256=YQh6eCAi4bsC_w9eGX0ZgnLoFL4z4efEc1knJsZhj4k,13842
|
|
31
31
|
howler/api/v1/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
32
32
|
howler/api/v1/utils/etag.py,sha256=IGB4WUkecHbBt0KKLQFCxCn0mN3T_aSPG0y45l_zT9I,3431
|
|
33
33
|
howler/api/v1/view.py,sha256=VyizfGehsuoPyn7iswGPKCLcbSQoO1ipRsbr91nSDKk,8228
|
|
34
|
-
howler/app.py,sha256=
|
|
34
|
+
howler/app.py,sha256=76WSFUM4UKX2UpvDHfgKP8DI3wavLpXGlk2QLHqH1x8,7043
|
|
35
35
|
howler/common/README.md,sha256=lgnrAdgnOADmmfRplhbfYD7jU627nr3zO-fJ6N4Nbcs,6577
|
|
36
36
|
howler/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
37
37
|
howler/common/classification.py,sha256=AUl33PJX6O9d9XzxHbyyJah2ffD7Q7J2yYqIwz7OqKY,39342
|
|
@@ -88,7 +88,7 @@ howler/odm/README.md,sha256=Ihc_DyjVQlLaIOEbPoQNPkum9Ecn8kn37-PMFQsX77s,5645
|
|
|
88
88
|
howler/odm/__init__.py,sha256=1n6vgBOrFcCHSBFysqgODERvqP7s5DIeJe8N8UeE5pM,44
|
|
89
89
|
howler/odm/base.py,sha256=UxWDNokfNIuQK-SQz8gjIznhc8lLQe6eJTF_7aIbwqQ,51620
|
|
90
90
|
howler/odm/charter.txt,sha256=-Wgrv7nqugZmeQknJk0_m6klLJStjVbuqKbi_KaDinQ,15277
|
|
91
|
-
howler/odm/helper.py,sha256=
|
|
91
|
+
howler/odm/helper.py,sha256=EELMg3pvE7Kb9VDeKSYJQHiq6uCO2YuS095CPRgWrEM,13927
|
|
92
92
|
howler/odm/howler_enum.py,sha256=JzRK3_adlhvfkoGdMZD1jgOwlneZs8-x7OxGEj3zcpY,768
|
|
93
93
|
howler/odm/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
94
94
|
howler/odm/models/action.py,sha256=V9tgMCg1ewu8mOngTcA6QAPwaR0NIT71VHglpRFYmS4,1235
|
|
@@ -97,7 +97,7 @@ howler/odm/models/assemblyline.py,sha256=_wcTiX3A6bhA2SGlK9tDF0v-uwLpIabXE8j2Fw9
|
|
|
97
97
|
howler/odm/models/aws.py,sha256=pJVadJqubdgT27riCfp7bEKVP4XsMZB0ZUnKAbmCMd0,895
|
|
98
98
|
howler/odm/models/azure.py,sha256=o7MZMMo9jh1SB8xXCajl_YSKP2nnnWsjx_DPT6LnQKg,710
|
|
99
99
|
howler/odm/models/cbs.py,sha256=onUiJOGUxK3iy_-4XkGGwHxFiFq9Td_p59Kum4XaR-w,1366
|
|
100
|
-
howler/odm/models/config.py,sha256=
|
|
100
|
+
howler/odm/models/config.py,sha256=KO_-AdTLL1NPHoEHoKqwHiOYa1kCZ-7YX3IcY9-UAsI,21723
|
|
101
101
|
howler/odm/models/dossier.py,sha256=Ob2qROrG2-DYzmVo2XVe4NJ8HjWGCoRAu2gPo6p9XGU,1244
|
|
102
102
|
howler/odm/models/ecs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
103
103
|
howler/odm/models/ecs/agent.py,sha256=idSooyFCLuQAB7_RyEWTYW4-x9w5a3wpy2ct_-EDRQs,713
|
|
@@ -143,7 +143,7 @@ howler/odm/models/howler_data.py,sha256=3bzKQ_vSP_rK20S_U4PkXzsmBL1uqSaIOHZmoPuR
|
|
|
143
143
|
howler/odm/models/lead.py,sha256=lqapGWZ4u22Asib48o7wAzramnFY9EkRybmb4olsyrA,904
|
|
144
144
|
howler/odm/models/localized_label.py,sha256=G7gfQ1cngiI4KprqldHWE1KHkAgK4AG_JsfHxVRdsRs,361
|
|
145
145
|
howler/odm/models/overview.py,sha256=kvZcMYPDlkJEGa0L1jq9pG0RFjLOVudC64-2GTWVu2w,684
|
|
146
|
-
howler/odm/models/pivot.py,sha256=
|
|
146
|
+
howler/odm/models/pivot.py,sha256=ZewcGh91xbE64snZ5Ahz2So1onAqO8U9H4CFe6jBOXs,1405
|
|
147
147
|
howler/odm/models/template.py,sha256=-Tqq_36qD_3nQ4jv13OPeH_EyyERKhc55wT03CU-Kbk,979
|
|
148
148
|
howler/odm/models/user.py,sha256=3V7cLxxHJwWfTsEdZ7-QZT_-PQL7H_RJ3buQ8AraGzQ,3052
|
|
149
149
|
howler/odm/models/view.py,sha256=kmaJOXhR4prki5o0gBirs1dqGcQK3b9ATysL_kNoku0,1308
|
|
@@ -166,14 +166,14 @@ howler/remote/datatypes/queues/named.py,sha256=IypPN0ZnRTlWCZ0Wm6BAyoNCoGlLxUPyG
|
|
|
166
166
|
howler/remote/datatypes/queues/priority.py,sha256=rfIK0nVFtivY82a6Er-1CZMzvYEVGyPcUmYrks1nzfI,7554
|
|
167
167
|
howler/remote/datatypes/set.py,sha256=iJ-QrrkKRhEUyOYmRz21TUTitatvrNyp5aMJwXkCdqc,3538
|
|
168
168
|
howler/remote/datatypes/user_quota_tracker.py,sha256=butA5RlV0XU9y6GtqoEryaJJZq-LAgoDhCJpdbHBFm0,1920
|
|
169
|
-
howler/security/__init__.py,sha256=
|
|
169
|
+
howler/security/__init__.py,sha256=v5gQi5TUA4W4vlU3Adzxw8Tq3gCBzQPSF3F9kLDnZgc,11668
|
|
170
170
|
howler/security/socket.py,sha256=zEyWQh7IMINNpYMynV-PLy_-HQLpgouUvztAKrTHEfU,3714
|
|
171
171
|
howler/security/utils.py,sha256=MweKs9T--Z2w6hZNUfEAik4FsvXPNCskYF1vaQVt6_8,5309
|
|
172
172
|
howler/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
173
173
|
howler/services/action_service.py,sha256=AuNRvZs7sFGUveaoptD8ouzs39jdZJidmsp-fIocZo4,3977
|
|
174
174
|
howler/services/analytic_service.py,sha256=pOJRCDn6I523XSVLBo49fL70Mz5VDgjJ5CBYhemQeBk,4272
|
|
175
175
|
howler/services/auth_service.py,sha256=TX51z-Fu4i3JvxIdU1JsJ3vzgGPVWNL1lNQJMxKpkZ8,11678
|
|
176
|
-
howler/services/config_service.py,sha256=
|
|
176
|
+
howler/services/config_service.py,sha256=J5FCttgsHr6kMLGAPIW-8r8aGYwmX4YzsOyBG6yoGGM,4489
|
|
177
177
|
howler/services/dossier_service.py,sha256=5jq7KZMgBNh165rN5pZD_oVuZP-oBsdACHDSV74ps-I,9839
|
|
178
178
|
howler/services/event_service.py,sha256=4PG2iBXjh1V8QnXcbUZSiKJeHs6V9hRWT9SKrzQFIPY,2864
|
|
179
179
|
howler/services/hit_service.py,sha256=VNsEhi6Tcoqkjsw8G71FWBj7xcKwdvET9mITYAKmzsM,32278
|
|
@@ -194,7 +194,7 @@ howler/utils/path.py,sha256=DfOU4i4zSs4wchHoE8iE7aWVLkTxiC_JRGepF2hBYBk,690
|
|
|
194
194
|
howler/utils/socket_utils.py,sha256=nz1SklC9xBHUSfHyTJjpq3mbozX1GDf01WzdGxfaUII,2212
|
|
195
195
|
howler/utils/str_utils.py,sha256=HE8Hqh2HlOLaj16w0H9zKOyDJLp-f1LQ50y_WeGZaEk,8389
|
|
196
196
|
howler/utils/uid.py,sha256=p9dsqyvZ-lpiAuzZWCPCeEM99kdk0Ly9czf04HNdSuw,1341
|
|
197
|
-
howler_api-2.13.0.
|
|
198
|
-
howler_api-2.13.0.
|
|
199
|
-
howler_api-2.13.0.
|
|
200
|
-
howler_api-2.13.0.
|
|
197
|
+
howler_api-2.13.0.dev343.dist-info/METADATA,sha256=w8tdTek4q5qOcYrGXknBA5mbX0wvBVdvgciHKtPg56A,2805
|
|
198
|
+
howler_api-2.13.0.dev343.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
199
|
+
howler_api-2.13.0.dev343.dist-info/entry_points.txt,sha256=Lu9SBGvwe0wczJHmc-RudC24lmQk7tv3ZBXon9RIihg,259
|
|
200
|
+
howler_api-2.13.0.dev343.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|