howler-api 3.0.0.dev374__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 +168 -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/clue.py +99 -0
- howler/api/v1/configs.py +58 -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 +788 -0
- howler/api/v1/template.py +206 -0
- howler/api/v1/tool.py +183 -0
- howler/api/v1/user.py +416 -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 +125 -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/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 +2342 -0
- howler/datastore/constants.py +119 -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 +215 -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 +66 -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 +1543 -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 +24 -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 +609 -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 +331 -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-3.0.0.dev374.dist-info/METADATA +71 -0
- howler_api-3.0.0.dev374.dist-info/RECORD +198 -0
- howler_api-3.0.0.dev374.dist-info/WHEEL +4 -0
- howler_api-3.0.0.dev374.dist-info/entry_points.txt +8 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
from flask import request
|
|
6
|
+
|
|
7
|
+
from howler.common.logging.format import HWL_AUDIT_FORMAT, HWL_DATE_FORMAT, HWL_ISO_DATE_FORMAT, HWL_LOG_FORMAT
|
|
8
|
+
from howler.config import DEBUG, config
|
|
9
|
+
|
|
10
|
+
AUDIT = config.ui.audit
|
|
11
|
+
|
|
12
|
+
AUDIT_KW_TARGET = [
|
|
13
|
+
"sid",
|
|
14
|
+
"sha256",
|
|
15
|
+
"copy_sid",
|
|
16
|
+
"filter",
|
|
17
|
+
"query",
|
|
18
|
+
"username",
|
|
19
|
+
"group",
|
|
20
|
+
"rev",
|
|
21
|
+
"wq_id",
|
|
22
|
+
"index",
|
|
23
|
+
"cache_key",
|
|
24
|
+
"alert_key",
|
|
25
|
+
"alert_id",
|
|
26
|
+
"url",
|
|
27
|
+
"q",
|
|
28
|
+
"fq",
|
|
29
|
+
"file_hash",
|
|
30
|
+
"heuristic_id",
|
|
31
|
+
"error_key",
|
|
32
|
+
"mac",
|
|
33
|
+
"vm_type",
|
|
34
|
+
"vm_name",
|
|
35
|
+
"config_name",
|
|
36
|
+
"servicename",
|
|
37
|
+
"vm",
|
|
38
|
+
"transition",
|
|
39
|
+
"data",
|
|
40
|
+
"id",
|
|
41
|
+
"comment_id",
|
|
42
|
+
"label_set",
|
|
43
|
+
"tool_name",
|
|
44
|
+
"operation_id",
|
|
45
|
+
"category",
|
|
46
|
+
"label",
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
AUDIT_LOG = logging.getLogger("howler.api.audit")
|
|
50
|
+
AUDIT_LOG.propagate = False
|
|
51
|
+
|
|
52
|
+
if AUDIT:
|
|
53
|
+
AUDIT_LOG.setLevel(logging.DEBUG)
|
|
54
|
+
|
|
55
|
+
if not os.path.exists(config.logging.log_directory):
|
|
56
|
+
os.makedirs(config.logging.log_directory)
|
|
57
|
+
|
|
58
|
+
fh = logging.FileHandler(os.path.join(config.logging.log_directory, "hwl_audit.log"))
|
|
59
|
+
fh.setLevel(logging.DEBUG)
|
|
60
|
+
fh.setFormatter(
|
|
61
|
+
logging.Formatter(
|
|
62
|
+
HWL_LOG_FORMAT if DEBUG else HWL_AUDIT_FORMAT,
|
|
63
|
+
HWL_DATE_FORMAT if DEBUG else HWL_ISO_DATE_FORMAT,
|
|
64
|
+
)
|
|
65
|
+
)
|
|
66
|
+
AUDIT_LOG.addHandler(fh)
|
|
67
|
+
|
|
68
|
+
ch = logging.StreamHandler(sys.stdout)
|
|
69
|
+
ch.setLevel(logging.INFO)
|
|
70
|
+
ch.setFormatter(
|
|
71
|
+
logging.Formatter(
|
|
72
|
+
HWL_LOG_FORMAT if DEBUG else HWL_AUDIT_FORMAT,
|
|
73
|
+
HWL_DATE_FORMAT if DEBUG else HWL_ISO_DATE_FORMAT,
|
|
74
|
+
)
|
|
75
|
+
)
|
|
76
|
+
AUDIT_LOG.addHandler(ch)
|
|
77
|
+
|
|
78
|
+
#########################
|
|
79
|
+
# End of prepare logger #
|
|
80
|
+
#########################
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def audit(args, kwargs, logged_in_uname, user, func, impersonator=None):
|
|
84
|
+
"""Log audit information for a given function executed by a given user."""
|
|
85
|
+
try:
|
|
86
|
+
json_blob = request.json
|
|
87
|
+
if not isinstance(json_blob, dict):
|
|
88
|
+
json_blob = {}
|
|
89
|
+
except Exception:
|
|
90
|
+
json_blob = {}
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
req_args = ["%s='%s'" % (k, v) for k, v in request.args.items() if k in AUDIT_KW_TARGET]
|
|
94
|
+
except RuntimeError:
|
|
95
|
+
req_args = []
|
|
96
|
+
|
|
97
|
+
params_list = (
|
|
98
|
+
list(args)
|
|
99
|
+
+ ["%s='%s'" % (k, v) for k, v in kwargs.items() if k in AUDIT_KW_TARGET]
|
|
100
|
+
+ req_args
|
|
101
|
+
+ ["%s='%s'" % (k, v) for k, v in json_blob.items() if k in AUDIT_KW_TARGET]
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
if impersonator:
|
|
105
|
+
audit_user = f"{impersonator} on behalf of {logged_in_uname}"
|
|
106
|
+
else:
|
|
107
|
+
audit_user = logged_in_uname
|
|
108
|
+
if DEBUG:
|
|
109
|
+
# In debug mode, you'll get an output like:
|
|
110
|
+
# 23/03/20 14:26:56 DEBUG howler.api.audit | goose - search(index='...', query='...')
|
|
111
|
+
AUDIT_LOG.debug(
|
|
112
|
+
"%s - %s(%s)",
|
|
113
|
+
audit_user,
|
|
114
|
+
func.__name__,
|
|
115
|
+
", ".join(params_list),
|
|
116
|
+
)
|
|
117
|
+
else:
|
|
118
|
+
# In prod, you'll get an output like:
|
|
119
|
+
# {
|
|
120
|
+
# "date": "2023-03-20T18:33:27-0400",
|
|
121
|
+
# "type": "audit",
|
|
122
|
+
# "app_name": "howler",
|
|
123
|
+
# "api": "howler.api.audit",
|
|
124
|
+
# "severity": "INFO",
|
|
125
|
+
# "user": "goose",
|
|
126
|
+
# "function": "search(index='hit', query='howler.escalation:alert AND howler.status:open')",
|
|
127
|
+
# "method": "POST",
|
|
128
|
+
# "path": "/api/v1/search/hit/"
|
|
129
|
+
# }
|
|
130
|
+
AUDIT_LOG.info(
|
|
131
|
+
"",
|
|
132
|
+
extra={
|
|
133
|
+
"user": audit_user,
|
|
134
|
+
"function": f"{func.__name__}({', '.join(params_list)})",
|
|
135
|
+
"method": request.method,
|
|
136
|
+
"path": request.path,
|
|
137
|
+
},
|
|
138
|
+
)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
APP_NAME = os.environ.get("APP_NAME", "howler")
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
from howler.common.net import get_hostname
|
|
8
|
+
|
|
9
|
+
hostname = get_hostname()
|
|
10
|
+
except Exception:
|
|
11
|
+
hostname = "unknownhost"
|
|
12
|
+
|
|
13
|
+
HWL_SYSLOG_FORMAT = f"HWL %(levelname)8s {hostname} %(process)5d %(name)40s | %(message)s"
|
|
14
|
+
HWL_LOG_FORMAT = "%(asctime)s %(levelname)s %(name)s | %(message)s"
|
|
15
|
+
HWL_DATE_FORMAT = "%y/%m/%d %H:%M:%S"
|
|
16
|
+
HWL_JSON_FORMAT = (
|
|
17
|
+
f"{{"
|
|
18
|
+
f'"@timestamp": "%(asctime)s", '
|
|
19
|
+
f'"event": {{ "module": "{APP_NAME}", "dataset": "%(name)s" }}, '
|
|
20
|
+
f'"host": {{ "hostname": "{hostname}" }}, '
|
|
21
|
+
f'"log": {{ "level": "%(levelname)s", "logger": "%(name)s" }}, '
|
|
22
|
+
f'"process": {{ "pid": "%(process)d" }}, '
|
|
23
|
+
f'"message": %(message)s}}'
|
|
24
|
+
)
|
|
25
|
+
HWL_ISO_DATE_FORMAT = "%Y-%m-%dT%H:%M:%S%z"
|
|
26
|
+
HWL_AUDIT_FORMAT = json.dumps(
|
|
27
|
+
{
|
|
28
|
+
"date": "%(asctime)s",
|
|
29
|
+
"type": "audit",
|
|
30
|
+
"app_name": APP_NAME,
|
|
31
|
+
"api": "howler.api.audit",
|
|
32
|
+
"severity": "%(levelname)s",
|
|
33
|
+
"user": "%(user)s",
|
|
34
|
+
"function": "%(function)s",
|
|
35
|
+
"method": "%(method)s",
|
|
36
|
+
"path": "%(path)s",
|
|
37
|
+
}
|
|
38
|
+
).replace('"msg"', "%(message)s")
|
howler/common/net.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import socket
|
|
2
|
+
import uuid
|
|
3
|
+
from ipaddress import IPv4Network, ip_address
|
|
4
|
+
from typing import Union
|
|
5
|
+
|
|
6
|
+
from howler.common.net_static import TLDS_ALPHA_BY_DOMAIN
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def is_valid_port(value: Union[int, str, float]) -> bool:
|
|
10
|
+
"Check if a port is valid"
|
|
11
|
+
try:
|
|
12
|
+
if 1 <= int(value) <= 65535:
|
|
13
|
+
return True
|
|
14
|
+
except ValueError:
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
return False
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def is_valid_domain(domain: str) -> bool:
|
|
21
|
+
"Check if a domain is valid"
|
|
22
|
+
if "@" in domain:
|
|
23
|
+
return False
|
|
24
|
+
|
|
25
|
+
if "." in domain:
|
|
26
|
+
tld = domain.split(".")[-1]
|
|
27
|
+
return tld.upper() in TLDS_ALPHA_BY_DOMAIN
|
|
28
|
+
|
|
29
|
+
return False
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def is_valid_ip(ip: str) -> bool:
|
|
33
|
+
"Check if an ip is valid"
|
|
34
|
+
parts = ip.split(".")
|
|
35
|
+
if len(parts) == 4:
|
|
36
|
+
for p in parts:
|
|
37
|
+
try:
|
|
38
|
+
if not (0 <= int(p) <= 255):
|
|
39
|
+
return False
|
|
40
|
+
except ValueError:
|
|
41
|
+
return False
|
|
42
|
+
|
|
43
|
+
if int(parts[0]) == 0:
|
|
44
|
+
return False
|
|
45
|
+
|
|
46
|
+
if int(parts[3]) == 0:
|
|
47
|
+
return False
|
|
48
|
+
|
|
49
|
+
return True
|
|
50
|
+
|
|
51
|
+
return False
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def is_ip_in_network(ip: str, network: IPv4Network) -> bool:
|
|
55
|
+
"Check if an ip is in a given network"
|
|
56
|
+
if not is_valid_ip(ip):
|
|
57
|
+
return False
|
|
58
|
+
|
|
59
|
+
return ip_address(ip) in network
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def is_valid_email(email: str) -> bool:
|
|
63
|
+
"Check if an email is valid"
|
|
64
|
+
parts = email.split("@")
|
|
65
|
+
if len(parts) == 2:
|
|
66
|
+
if is_valid_domain(parts[1]):
|
|
67
|
+
return True
|
|
68
|
+
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def get_hostname() -> str:
|
|
73
|
+
"Get the hostname of the computer howler is running on"
|
|
74
|
+
return socket.gethostname()
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def get_mac_address() -> str:
|
|
78
|
+
"Get the mac address of the computer howler is running on"
|
|
79
|
+
return "".join(["{0:02x}".format((uuid.getnode() >> i) & 0xFF) for i in range(0, 8 * 6, 8)][::-1]).upper()
|