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.

Files changed (198) hide show
  1. howler/__init__.py +0 -0
  2. howler/actions/__init__.py +168 -0
  3. howler/actions/add_label.py +111 -0
  4. howler/actions/add_to_bundle.py +159 -0
  5. howler/actions/change_field.py +76 -0
  6. howler/actions/demote.py +160 -0
  7. howler/actions/example_plugin.py +104 -0
  8. howler/actions/prioritization.py +93 -0
  9. howler/actions/promote.py +147 -0
  10. howler/actions/remove_from_bundle.py +133 -0
  11. howler/actions/remove_label.py +111 -0
  12. howler/actions/transition.py +200 -0
  13. howler/api/__init__.py +249 -0
  14. howler/api/base.py +88 -0
  15. howler/api/socket.py +114 -0
  16. howler/api/v1/__init__.py +97 -0
  17. howler/api/v1/action.py +372 -0
  18. howler/api/v1/analytic.py +748 -0
  19. howler/api/v1/auth.py +382 -0
  20. howler/api/v1/clue.py +99 -0
  21. howler/api/v1/configs.py +58 -0
  22. howler/api/v1/dossier.py +222 -0
  23. howler/api/v1/help.py +28 -0
  24. howler/api/v1/hit.py +1181 -0
  25. howler/api/v1/notebook.py +82 -0
  26. howler/api/v1/overview.py +191 -0
  27. howler/api/v1/search.py +788 -0
  28. howler/api/v1/template.py +206 -0
  29. howler/api/v1/tool.py +183 -0
  30. howler/api/v1/user.py +416 -0
  31. howler/api/v1/utils/__init__.py +0 -0
  32. howler/api/v1/utils/etag.py +84 -0
  33. howler/api/v1/view.py +288 -0
  34. howler/app.py +235 -0
  35. howler/common/README.md +125 -0
  36. howler/common/__init__.py +0 -0
  37. howler/common/classification.py +979 -0
  38. howler/common/classification.yml +107 -0
  39. howler/common/exceptions.py +167 -0
  40. howler/common/loader.py +154 -0
  41. howler/common/logging/__init__.py +241 -0
  42. howler/common/logging/audit.py +138 -0
  43. howler/common/logging/format.py +38 -0
  44. howler/common/net.py +79 -0
  45. howler/common/net_static.py +1494 -0
  46. howler/common/random_user.py +316 -0
  47. howler/common/swagger.py +117 -0
  48. howler/config.py +64 -0
  49. howler/cronjobs/__init__.py +29 -0
  50. howler/cronjobs/retention.py +61 -0
  51. howler/cronjobs/rules.py +274 -0
  52. howler/cronjobs/view_cleanup.py +88 -0
  53. howler/datastore/README.md +112 -0
  54. howler/datastore/__init__.py +0 -0
  55. howler/datastore/bulk.py +72 -0
  56. howler/datastore/collection.py +2342 -0
  57. howler/datastore/constants.py +119 -0
  58. howler/datastore/exceptions.py +41 -0
  59. howler/datastore/howler_store.py +105 -0
  60. howler/datastore/migrations/fix_process.py +41 -0
  61. howler/datastore/operations.py +130 -0
  62. howler/datastore/schemas.py +90 -0
  63. howler/datastore/store.py +231 -0
  64. howler/datastore/support/__init__.py +0 -0
  65. howler/datastore/support/build.py +215 -0
  66. howler/datastore/support/schemas.py +90 -0
  67. howler/datastore/types.py +22 -0
  68. howler/error.py +91 -0
  69. howler/external/__init__.py +0 -0
  70. howler/external/generate_mitre.py +96 -0
  71. howler/external/generate_sigma_rules.py +31 -0
  72. howler/external/generate_tlds.py +47 -0
  73. howler/external/reindex_data.py +66 -0
  74. howler/external/wipe_databases.py +58 -0
  75. howler/gunicorn_config.py +25 -0
  76. howler/healthz.py +47 -0
  77. howler/helper/__init__.py +0 -0
  78. howler/helper/azure.py +50 -0
  79. howler/helper/discover.py +59 -0
  80. howler/helper/hit.py +236 -0
  81. howler/helper/oauth.py +247 -0
  82. howler/helper/search.py +92 -0
  83. howler/helper/workflow.py +110 -0
  84. howler/helper/ws.py +378 -0
  85. howler/odm/README.md +102 -0
  86. howler/odm/__init__.py +1 -0
  87. howler/odm/base.py +1543 -0
  88. howler/odm/charter.txt +146 -0
  89. howler/odm/helper.py +416 -0
  90. howler/odm/howler_enum.py +25 -0
  91. howler/odm/models/__init__.py +0 -0
  92. howler/odm/models/action.py +33 -0
  93. howler/odm/models/analytic.py +90 -0
  94. howler/odm/models/assemblyline.py +48 -0
  95. howler/odm/models/aws.py +23 -0
  96. howler/odm/models/azure.py +16 -0
  97. howler/odm/models/cbs.py +44 -0
  98. howler/odm/models/config.py +558 -0
  99. howler/odm/models/dossier.py +33 -0
  100. howler/odm/models/ecs/__init__.py +0 -0
  101. howler/odm/models/ecs/agent.py +17 -0
  102. howler/odm/models/ecs/autonomous_system.py +16 -0
  103. howler/odm/models/ecs/client.py +149 -0
  104. howler/odm/models/ecs/cloud.py +141 -0
  105. howler/odm/models/ecs/code_signature.py +27 -0
  106. howler/odm/models/ecs/container.py +32 -0
  107. howler/odm/models/ecs/dns.py +62 -0
  108. howler/odm/models/ecs/egress.py +10 -0
  109. howler/odm/models/ecs/elf.py +74 -0
  110. howler/odm/models/ecs/email.py +122 -0
  111. howler/odm/models/ecs/error.py +14 -0
  112. howler/odm/models/ecs/event.py +140 -0
  113. howler/odm/models/ecs/faas.py +24 -0
  114. howler/odm/models/ecs/file.py +84 -0
  115. howler/odm/models/ecs/geo.py +30 -0
  116. howler/odm/models/ecs/group.py +18 -0
  117. howler/odm/models/ecs/hash.py +16 -0
  118. howler/odm/models/ecs/host.py +17 -0
  119. howler/odm/models/ecs/http.py +37 -0
  120. howler/odm/models/ecs/ingress.py +12 -0
  121. howler/odm/models/ecs/interface.py +21 -0
  122. howler/odm/models/ecs/network.py +30 -0
  123. howler/odm/models/ecs/observer.py +45 -0
  124. howler/odm/models/ecs/organization.py +12 -0
  125. howler/odm/models/ecs/os.py +21 -0
  126. howler/odm/models/ecs/pe.py +17 -0
  127. howler/odm/models/ecs/process.py +216 -0
  128. howler/odm/models/ecs/registry.py +26 -0
  129. howler/odm/models/ecs/related.py +45 -0
  130. howler/odm/models/ecs/rule.py +51 -0
  131. howler/odm/models/ecs/server.py +24 -0
  132. howler/odm/models/ecs/threat.py +247 -0
  133. howler/odm/models/ecs/tls.py +58 -0
  134. howler/odm/models/ecs/url.py +51 -0
  135. howler/odm/models/ecs/user.py +57 -0
  136. howler/odm/models/ecs/user_agent.py +20 -0
  137. howler/odm/models/ecs/vulnerability.py +41 -0
  138. howler/odm/models/gcp.py +16 -0
  139. howler/odm/models/hit.py +356 -0
  140. howler/odm/models/howler_data.py +328 -0
  141. howler/odm/models/lead.py +24 -0
  142. howler/odm/models/localized_label.py +13 -0
  143. howler/odm/models/overview.py +16 -0
  144. howler/odm/models/pivot.py +40 -0
  145. howler/odm/models/template.py +24 -0
  146. howler/odm/models/user.py +83 -0
  147. howler/odm/models/view.py +34 -0
  148. howler/odm/random_data.py +888 -0
  149. howler/odm/randomizer.py +609 -0
  150. howler/patched.py +5 -0
  151. howler/plugins/__init__.py +25 -0
  152. howler/plugins/config.py +123 -0
  153. howler/remote/__init__.py +0 -0
  154. howler/remote/datatypes/README.md +355 -0
  155. howler/remote/datatypes/__init__.py +98 -0
  156. howler/remote/datatypes/counters.py +63 -0
  157. howler/remote/datatypes/events.py +66 -0
  158. howler/remote/datatypes/hash.py +206 -0
  159. howler/remote/datatypes/lock.py +42 -0
  160. howler/remote/datatypes/queues/__init__.py +0 -0
  161. howler/remote/datatypes/queues/comms.py +59 -0
  162. howler/remote/datatypes/queues/multi.py +32 -0
  163. howler/remote/datatypes/queues/named.py +93 -0
  164. howler/remote/datatypes/queues/priority.py +215 -0
  165. howler/remote/datatypes/set.py +118 -0
  166. howler/remote/datatypes/user_quota_tracker.py +54 -0
  167. howler/security/__init__.py +253 -0
  168. howler/security/socket.py +108 -0
  169. howler/security/utils.py +185 -0
  170. howler/services/__init__.py +0 -0
  171. howler/services/action_service.py +111 -0
  172. howler/services/analytic_service.py +128 -0
  173. howler/services/auth_service.py +323 -0
  174. howler/services/config_service.py +128 -0
  175. howler/services/dossier_service.py +252 -0
  176. howler/services/event_service.py +93 -0
  177. howler/services/hit_service.py +893 -0
  178. howler/services/jwt_service.py +158 -0
  179. howler/services/lucene_service.py +286 -0
  180. howler/services/notebook_service.py +119 -0
  181. howler/services/overview_service.py +44 -0
  182. howler/services/template_service.py +45 -0
  183. howler/services/user_service.py +331 -0
  184. howler/utils/__init__.py +0 -0
  185. howler/utils/annotations.py +28 -0
  186. howler/utils/chunk.py +38 -0
  187. howler/utils/dict_utils.py +200 -0
  188. howler/utils/isotime.py +17 -0
  189. howler/utils/list_utils.py +11 -0
  190. howler/utils/lucene.py +77 -0
  191. howler/utils/path.py +27 -0
  192. howler/utils/socket_utils.py +61 -0
  193. howler/utils/str_utils.py +256 -0
  194. howler/utils/uid.py +47 -0
  195. howler_api-3.0.0.dev374.dist-info/METADATA +71 -0
  196. howler_api-3.0.0.dev374.dist-info/RECORD +198 -0
  197. howler_api-3.0.0.dev374.dist-info/WHEEL +4 -0
  198. 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()