howler-sentinel-plugin 0.2.0.dev96__py3-none-any.whl → 0.2.0.dev98__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_sentinel_plugin-0.2.0.dev96.dist-info → howler_sentinel_plugin-0.2.0.dev98.dist-info}/METADATA +1 -1
- {howler_sentinel_plugin-0.2.0.dev96.dist-info → howler_sentinel_plugin-0.2.0.dev98.dist-info}/RECORD +8 -9
- sentinel/actions/azure_emit_hash.py +84 -0
- sentinel/actions/send_to_sentinel.py +15 -1
- sentinel/actions/update_defender_xdr_alert.py +1 -1
- sentinel/actions/ingestion.py +0 -59
- sentinel/actions/synchronization.py +0 -60
- {howler_sentinel_plugin-0.2.0.dev96.dist-info → howler_sentinel_plugin-0.2.0.dev98.dist-info}/LICENSE +0 -0
- {howler_sentinel_plugin-0.2.0.dev96.dist-info → howler_sentinel_plugin-0.2.0.dev98.dist-info}/WHEEL +0 -0
- {howler_sentinel_plugin-0.2.0.dev96.dist-info → howler_sentinel_plugin-0.2.0.dev98.dist-info}/entry_points.txt +0 -0
{howler_sentinel_plugin-0.2.0.dev96.dist-info → howler_sentinel_plugin-0.2.0.dev98.dist-info}/RECORD
RENAMED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
sentinel/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
sentinel/actions/
|
|
3
|
-
sentinel/actions/send_to_sentinel.py,sha256=
|
|
4
|
-
sentinel/actions/
|
|
5
|
-
sentinel/actions/update_defender_xdr_alert.py,sha256=hlCG2d2X19Zhtm1KGAqNHLG9a4yO53GZebRp--07D7c,6408
|
|
2
|
+
sentinel/actions/azure_emit_hash.py,sha256=52ZJNbaCT4OshM1EqK3y47KD_HCxqPqnEnc3XsgUqso,2582
|
|
3
|
+
sentinel/actions/send_to_sentinel.py,sha256=sg3LyP7IGApMftBU1TTZSUmCGtUI9PwvmI7UBj1Jrf0,3714
|
|
4
|
+
sentinel/actions/update_defender_xdr_alert.py,sha256=_Dtsi2pR5xN8ltzYGpCn9JbYnK731l6LSrGtERVfym4,6398
|
|
6
5
|
sentinel/mapping/sentinel_incident.py,sha256=3QBnP6qFpJgE3pHvx5VvFnB3m2TVOoWxs8OysDlJVV8,9547
|
|
7
6
|
sentinel/mapping/xdr_alert.py,sha256=UPoqdZsjUXmJz0dCf_qMlh9Jr0D2HcSNOFvbg8lE4wY,18250
|
|
8
7
|
sentinel/mapping/xdr_alert_evidence.py,sha256=q622G4eZwFR3TCj418ZCpE83DGVicrWIQZo8Gkj_3FM,31323
|
|
@@ -11,8 +10,8 @@ sentinel/odm/models/sentinel.py,sha256=XT3XdT92uoCV5vmY9dT1jmcxRyuu9vp1gE8AwZdKB
|
|
|
11
10
|
sentinel/routes/__init__.py,sha256=JYmKRwIfEsiPos1XuMQ2mlGDbxk6TN_cVEM0K_RNze4,130
|
|
12
11
|
sentinel/routes/ingest.py,sha256=_9OdOw_9nBJseKIBnmHDLjnqZ_bDdM4wfLpLrek4-ak,7018
|
|
13
12
|
sentinel/utils/tenant_utils.py,sha256=W7kBtxYNhs3vcgMf78eIRqiTpDtqjzEI2H2d0papQ_Q,1224
|
|
14
|
-
howler_sentinel_plugin-0.2.0.
|
|
15
|
-
howler_sentinel_plugin-0.2.0.
|
|
16
|
-
howler_sentinel_plugin-0.2.0.
|
|
17
|
-
howler_sentinel_plugin-0.2.0.
|
|
18
|
-
howler_sentinel_plugin-0.2.0.
|
|
13
|
+
howler_sentinel_plugin-0.2.0.dev98.dist-info/LICENSE,sha256=Wg2luVnxEkP2NSn11nh1US6W_nFFbICBAVTG9iG3t5M,1091
|
|
14
|
+
howler_sentinel_plugin-0.2.0.dev98.dist-info/METADATA,sha256=MLmv_F7Wd0W3um49eqIZXDUGvDfR1XV1mjBM_3WcW7o,748
|
|
15
|
+
howler_sentinel_plugin-0.2.0.dev98.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
16
|
+
howler_sentinel_plugin-0.2.0.dev98.dist-info/entry_points.txt,sha256=4IJyMY0V49s3Wp659ngN_7U8g66-czeKxI-_dNAFP5g,60
|
|
17
|
+
howler_sentinel_plugin-0.2.0.dev98.dist-info/RECORD,,
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
from howler.common.loader import datastore
|
|
5
|
+
from howler.odm.models.action import VALID_TRIGGERS
|
|
6
|
+
from howler.odm.models.hit import Hit
|
|
7
|
+
from pydash import get
|
|
8
|
+
|
|
9
|
+
OPERATION_ID = "azure_emit_hash"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def execute(query: str, field: str = "file.hash.sha256", **kwargs):
|
|
13
|
+
"Emit hashes to sentinel"
|
|
14
|
+
result = datastore().hit.search(query, rows=1)
|
|
15
|
+
hits = result["items"]
|
|
16
|
+
|
|
17
|
+
if len(hits) < 1:
|
|
18
|
+
return [
|
|
19
|
+
{
|
|
20
|
+
"query": query,
|
|
21
|
+
"outcome": "error",
|
|
22
|
+
"title": "No alert found",
|
|
23
|
+
"message": "No alerts exist in this query.",
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
report = []
|
|
28
|
+
hit = hits[0]
|
|
29
|
+
|
|
30
|
+
if result["total"] > 1:
|
|
31
|
+
report.append(
|
|
32
|
+
{
|
|
33
|
+
"query": f"{query} AND -howler.id:{hit.howler.id}",
|
|
34
|
+
"outcome": "skipped",
|
|
35
|
+
"title": "Action applies to a single alert",
|
|
36
|
+
"message": "This action supports execution against a single alert at once, not bulk execution.",
|
|
37
|
+
}
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
for hit in hits:
|
|
41
|
+
hash_value = get(hit, field)
|
|
42
|
+
if hash_value:
|
|
43
|
+
requests.post(
|
|
44
|
+
os.environ["SHA256_LOGIC_APP_URL"], # noqa: F821
|
|
45
|
+
json={
|
|
46
|
+
"indicator": hash_value,
|
|
47
|
+
"type": "FileSha256",
|
|
48
|
+
"description": "Sent from Howler",
|
|
49
|
+
"action": "alert",
|
|
50
|
+
"severity": "high",
|
|
51
|
+
},
|
|
52
|
+
timeout=5.0,
|
|
53
|
+
)
|
|
54
|
+
else:
|
|
55
|
+
report.append(
|
|
56
|
+
{
|
|
57
|
+
"query": f"howler.id:{hit.howler.id}",
|
|
58
|
+
"outcome": "error",
|
|
59
|
+
"title": "Hash does not exist on alert",
|
|
60
|
+
"message": f"The specified alert does not have a valid sha256 hash at path {field}.",
|
|
61
|
+
}
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def specification():
|
|
66
|
+
"Specify various properties of the action, such as title, descriptions, permissions and input steps."
|
|
67
|
+
return {
|
|
68
|
+
"id": OPERATION_ID,
|
|
69
|
+
"title": "Emit sha256 hash to Sentinel",
|
|
70
|
+
"priority": 8,
|
|
71
|
+
"i18nKey": "operations.add_label",
|
|
72
|
+
"description": {
|
|
73
|
+
"short": "Add a label to a hit",
|
|
74
|
+
"long": execute.__doc__,
|
|
75
|
+
},
|
|
76
|
+
"roles": ["automation_basic"],
|
|
77
|
+
"steps": [
|
|
78
|
+
{
|
|
79
|
+
"args": {"field": []},
|
|
80
|
+
"options": {"field": [field for field in Hit.flat_fields().keys() if field.endswith("sha256")]},
|
|
81
|
+
}
|
|
82
|
+
],
|
|
83
|
+
"triggers": VALID_TRIGGERS,
|
|
84
|
+
}
|
|
@@ -34,8 +34,22 @@ def execute(query: str, **kwargs):
|
|
|
34
34
|
return report
|
|
35
35
|
|
|
36
36
|
for hit in hits:
|
|
37
|
+
tenant_id = hit.azure.tenant_id
|
|
38
|
+
if not tenant_id and hit.organization.id:
|
|
39
|
+
tenant_id = hit.organization.id
|
|
40
|
+
elif not tenant_id:
|
|
41
|
+
report.append(
|
|
42
|
+
{
|
|
43
|
+
"query": f"howler.id:{hit.howler.id}",
|
|
44
|
+
"outcome": "skipped",
|
|
45
|
+
"title": "Azure Tenant ID is missing",
|
|
46
|
+
"message": "This alert does not have a set tenant ID.",
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
continue
|
|
50
|
+
|
|
37
51
|
try:
|
|
38
|
-
token, credentials = get_token(
|
|
52
|
+
token, credentials = get_token(tenant_id)
|
|
39
53
|
except HowlerRuntimeError as err:
|
|
40
54
|
logger.exception("Error on token fetching")
|
|
41
55
|
report.append(
|
sentinel/actions/ingestion.py
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
from howler.common.loader import datastore
|
|
2
|
-
from howler.datastore.operations import OdmHelper
|
|
3
|
-
from howler.odm.models.hit import Hit
|
|
4
|
-
|
|
5
|
-
hit_helper = OdmHelper(Hit)
|
|
6
|
-
|
|
7
|
-
OPERATION_ID = "sentinel_ingestion"
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def execute(query: str, **kwargs):
|
|
11
|
-
"""Handle ingestion of howler alerts into sentinel.
|
|
12
|
-
|
|
13
|
-
Args:
|
|
14
|
-
query (str): The query specifying alerts to ingest into sentinel.
|
|
15
|
-
"""
|
|
16
|
-
report = [
|
|
17
|
-
{
|
|
18
|
-
"query": query,
|
|
19
|
-
"outcome": "skipped",
|
|
20
|
-
"title": "Not Yet Implemented",
|
|
21
|
-
"message": "This functionality hasn't been implemented yet.",
|
|
22
|
-
}
|
|
23
|
-
]
|
|
24
|
-
|
|
25
|
-
ds = datastore()
|
|
26
|
-
|
|
27
|
-
skipped_hits = ds.hit.search(
|
|
28
|
-
f"({query}) AND _exists_:sentinel.id",
|
|
29
|
-
fl="howler.id",
|
|
30
|
-
)["items"]
|
|
31
|
-
|
|
32
|
-
if len(skipped_hits) > 0:
|
|
33
|
-
report.append(
|
|
34
|
-
{
|
|
35
|
-
"query": f"howler.id:({' OR '.join(h.howler.id for h in skipped_hits)})",
|
|
36
|
-
"outcome": "skipped",
|
|
37
|
-
"title": "Skipped Hit with matching sentinel alert",
|
|
38
|
-
"message": "These hits already have a matching incident in sentinel.",
|
|
39
|
-
}
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
print("Ingest alerts into sentinel that match this query:", query) # noqa: T201
|
|
43
|
-
|
|
44
|
-
return report
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def specification():
|
|
48
|
-
"""Specify various properties of the action, such as title, descriptions, permissions and input steps."""
|
|
49
|
-
return {
|
|
50
|
-
"id": OPERATION_ID,
|
|
51
|
-
"title": "Ingest into Sentinel",
|
|
52
|
-
"priority": 15,
|
|
53
|
-
"description": {
|
|
54
|
-
"short": "Ingest alerts into sentinel",
|
|
55
|
-
"long": execute.__doc__,
|
|
56
|
-
},
|
|
57
|
-
"roles": ["automation_basic"],
|
|
58
|
-
"triggers": ["create"],
|
|
59
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
from howler.common.loader import datastore
|
|
2
|
-
from howler.datastore.operations import OdmHelper
|
|
3
|
-
from howler.odm.models.action import VALID_TRIGGERS
|
|
4
|
-
from howler.odm.models.hit import Hit
|
|
5
|
-
|
|
6
|
-
hit_helper = OdmHelper(Hit)
|
|
7
|
-
|
|
8
|
-
OPERATION_ID = "sentinel_synchronization"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def execute(query: str, **kwargs):
|
|
12
|
-
"""Handle synchronization of howler alerts into sentinel.
|
|
13
|
-
|
|
14
|
-
Args:
|
|
15
|
-
query (str): The query specifying alerts to syncrhonize with sentinel.
|
|
16
|
-
"""
|
|
17
|
-
report = [
|
|
18
|
-
{
|
|
19
|
-
"query": query,
|
|
20
|
-
"outcome": "skipped",
|
|
21
|
-
"title": "Not Yet Implemented",
|
|
22
|
-
"message": "This functionality hasn't been implemented yet.",
|
|
23
|
-
}
|
|
24
|
-
]
|
|
25
|
-
|
|
26
|
-
ds = datastore()
|
|
27
|
-
|
|
28
|
-
skipped_hits = ds.hit.search(
|
|
29
|
-
f"({query}) AND -_exists_:sentinel.id",
|
|
30
|
-
fl="howler.id",
|
|
31
|
-
)["items"]
|
|
32
|
-
|
|
33
|
-
if len(skipped_hits) > 0:
|
|
34
|
-
report.append(
|
|
35
|
-
{
|
|
36
|
-
"query": f"howler.id:({' OR '.join(h.howler.id for h in skipped_hits)})",
|
|
37
|
-
"outcome": "skipped",
|
|
38
|
-
"title": "No matching sentinel alert",
|
|
39
|
-
"message": "These hits do not have a matching incident in sentinel.",
|
|
40
|
-
}
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
print("Update alerts into sentinel that match this query:", f"({query}) AND _exists_:sentinel.id") # noqa: T201
|
|
44
|
-
|
|
45
|
-
return report
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def specification():
|
|
49
|
-
"""Specify various properties of the action, such as title, descriptions, permissions and input steps."""
|
|
50
|
-
return {
|
|
51
|
-
"id": OPERATION_ID,
|
|
52
|
-
"title": "Synchronize with Sentinel",
|
|
53
|
-
"priority": 16,
|
|
54
|
-
"description": {
|
|
55
|
-
"short": "Synchronize alerts with sentinel",
|
|
56
|
-
"long": execute.__doc__,
|
|
57
|
-
},
|
|
58
|
-
"roles": ["automation_basic"],
|
|
59
|
-
"triggers": [trigger for trigger in VALID_TRIGGERS if trigger != "create"],
|
|
60
|
-
}
|
|
File without changes
|
{howler_sentinel_plugin-0.2.0.dev96.dist-info → howler_sentinel_plugin-0.2.0.dev98.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|