howler-sentinel-plugin 0.2.0.dev92__py3-none-any.whl → 0.2.0.dev95__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: howler-sentinel-plugin
3
- Version: 0.2.0.dev92
3
+ Version: 0.2.0.dev95
4
4
  Summary: A howler plugin for integration with Microsoft's Sentinel API
5
5
  License: MIT
6
6
  Author: CCCS
@@ -1,7 +1,8 @@
1
1
  sentinel/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  sentinel/actions/ingestion.py,sha256=_t7wrmozhoT_MmktDmNxYiXrA1Q0LCiDt0rTwHBkwbc,1669
3
- sentinel/actions/send_to_sentinel.py,sha256=qxYPHqsA2jbEiQG1qJdnIVQkdvxlL1gyPXNmOUVYnmk,3280
3
+ sentinel/actions/send_to_sentinel.py,sha256=REclVHxuG0Q5PIXZ-13crQhoz8BmJJJyTc6suzpq0m0,3227
4
4
  sentinel/actions/synchronization.py,sha256=g5c34410zINWb4fSEzj94drnk5alRj_ju9xMrB39z0s,1818
5
+ sentinel/actions/update_defender_xdr_alert.py,sha256=y6xCUFp6xpR1u1uAZd9CeK_sV9acwHamkXBTD_3cHg8,6848
5
6
  sentinel/mapping/sentinel_incident.py,sha256=3QBnP6qFpJgE3pHvx5VvFnB3m2TVOoWxs8OysDlJVV8,9547
6
7
  sentinel/mapping/xdr_alert.py,sha256=UPoqdZsjUXmJz0dCf_qMlh9Jr0D2HcSNOFvbg8lE4wY,18250
7
8
  sentinel/mapping/xdr_alert_evidence.py,sha256=q622G4eZwFR3TCj418ZCpE83DGVicrWIQZo8Gkj_3FM,31323
@@ -10,8 +11,8 @@ sentinel/odm/models/sentinel.py,sha256=XT3XdT92uoCV5vmY9dT1jmcxRyuu9vp1gE8AwZdKB
10
11
  sentinel/routes/__init__.py,sha256=JYmKRwIfEsiPos1XuMQ2mlGDbxk6TN_cVEM0K_RNze4,130
11
12
  sentinel/routes/ingest.py,sha256=_9OdOw_9nBJseKIBnmHDLjnqZ_bDdM4wfLpLrek4-ak,7018
12
13
  sentinel/utils/tenant_utils.py,sha256=W7kBtxYNhs3vcgMf78eIRqiTpDtqjzEI2H2d0papQ_Q,1224
13
- howler_sentinel_plugin-0.2.0.dev92.dist-info/LICENSE,sha256=Wg2luVnxEkP2NSn11nh1US6W_nFFbICBAVTG9iG3t5M,1091
14
- howler_sentinel_plugin-0.2.0.dev92.dist-info/METADATA,sha256=JxB76pRL1VAh9GWtDRhdo3ls9xtATvSwzpeU7vWubzU,748
15
- howler_sentinel_plugin-0.2.0.dev92.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
16
- howler_sentinel_plugin-0.2.0.dev92.dist-info/entry_points.txt,sha256=4IJyMY0V49s3Wp659ngN_7U8g66-czeKxI-_dNAFP5g,60
17
- howler_sentinel_plugin-0.2.0.dev92.dist-info/RECORD,,
14
+ howler_sentinel_plugin-0.2.0.dev95.dist-info/LICENSE,sha256=Wg2luVnxEkP2NSn11nh1US6W_nFFbICBAVTG9iG3t5M,1091
15
+ howler_sentinel_plugin-0.2.0.dev95.dist-info/METADATA,sha256=ivfqDB5AuHiiNXVlA5CcbdB0N-yS1qQuZ86gau729mM,748
16
+ howler_sentinel_plugin-0.2.0.dev95.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
17
+ howler_sentinel_plugin-0.2.0.dev95.dist-info/entry_points.txt,sha256=4IJyMY0V49s3Wp659ngN_7U8g66-czeKxI-_dNAFP5g,60
18
+ howler_sentinel_plugin-0.2.0.dev95.dist-info/RECORD,,
@@ -92,7 +92,6 @@ def specification():
92
92
  "id": OPERATION_ID,
93
93
  "title": "Send hit to Microsoft Sentinel",
94
94
  "priority": 8,
95
- "i18nKey": "Send hit to Microsoft Sentinel",
96
95
  "description": {
97
96
  "short": "Send hit to Microsoft Sentinel",
98
97
  "long": execute.__doc__,
@@ -0,0 +1,192 @@
1
+ import requests
2
+ from howler.common.exceptions import HowlerRuntimeError
3
+ from howler.common.loader import datastore
4
+ from howler.common.logging import get_logger
5
+ from howler.odm.models.action import VALID_TRIGGERS
6
+ from howler.odm.models.hit import Hit
7
+ from howler.odm.models.howler_data import Assessment, HitStatus
8
+
9
+ from sentinel.utils.tenant_utils import get_token
10
+
11
+ logger = get_logger(__file__)
12
+
13
+ OPERATION_ID = "update_defender_xdr_alert"
14
+
15
+ properties_map = {
16
+ "graph": {
17
+ "status": {
18
+ HitStatus.OPEN: "new",
19
+ HitStatus.IN_PROGRESS: "inProgress",
20
+ HitStatus.ON_HOLD: "inProgress",
21
+ HitStatus.RESOLVED: "resolved",
22
+ },
23
+ "classification": {
24
+ Assessment.AMBIGUOUS: "unknown",
25
+ Assessment.SECURITY: "informationalExpectedActivity",
26
+ Assessment.DEVELOPMENT: "informationalExpectedActivity",
27
+ Assessment.FALSE_POSITIVE: "falsePositive",
28
+ Assessment.LEGITIMATE: "informationalExpectedActivity",
29
+ Assessment.TRIVIAL: "falsePositive",
30
+ Assessment.RECON: "truePositive",
31
+ Assessment.ATTEMPT: "truePositive",
32
+ Assessment.COMPROMISE: "truePositive",
33
+ Assessment.MITIGATED: "truePositive",
34
+ None: "unknown",
35
+ },
36
+ "determination": {
37
+ Assessment.AMBIGUOUS: "unknown",
38
+ Assessment.SECURITY: "securityTesting",
39
+ Assessment.DEVELOPMENT: "confirmedUserActivity",
40
+ Assessment.FALSE_POSITIVE: "other",
41
+ Assessment.LEGITIMATE: "lineOfBusinessApplication",
42
+ Assessment.TRIVIAL: "other",
43
+ Assessment.RECON: "multiStagedAttack",
44
+ Assessment.ATTEMPT: "other",
45
+ Assessment.COMPROMISE: "maliciousUserActivity",
46
+ Assessment.MITIGATED: "other",
47
+ None: "unknown",
48
+ },
49
+ },
50
+ }
51
+
52
+
53
+ def execute(query: str, **kwargs):
54
+ """Update Microsoft Defender XDR alert.
55
+
56
+ Args:
57
+ query (str): The query on which to apply this automation.
58
+ """
59
+ report = []
60
+ ds = datastore()
61
+
62
+ hits: list[Hit] = ds.hit.search(query, as_obj=True)["items"]
63
+
64
+ if not hits:
65
+ report.append(
66
+ {
67
+ "query": query,
68
+ "outcome": "error",
69
+ "title": "No hits returned by query",
70
+ "message": f"No hits returned by '{query}'",
71
+ }
72
+ )
73
+ return report
74
+
75
+ for hit in hits:
76
+ try:
77
+ token, credentials = get_token(hit.azure.tenant_id)
78
+ except HowlerRuntimeError as err:
79
+ logger.exception("Error on token fetching")
80
+ report.append(
81
+ {
82
+ "query": f"howler.id:{hit.howler.id}",
83
+ "outcome": "error",
84
+ "title": "Invalid Credentials",
85
+ "message": err.message,
86
+ }
87
+ )
88
+ continue
89
+
90
+ token_request_url = f"https://login.microsoftonline.com/{hit.azure.tenant_id}/oauth2/v2.0/token"
91
+ data = {
92
+ "grant_type": "client_credentials",
93
+ "client_id": credentials["client_id"],
94
+ "client_secret": credentials["client_secret"],
95
+ "scope": "https://graph.microsoft.com/.default",
96
+ }
97
+ response = requests.post(token_request_url, data=data, timeout=5.0)
98
+
99
+ if not response.ok:
100
+ logger.warning("Failed to authenticate to Microsoft Graph.")
101
+ report.append(
102
+ {
103
+ "query": query,
104
+ "outcome": "error",
105
+ "title": "Authentication failed",
106
+ "message": f"Authentication to Microsoft Graph API failed with status code {response.status_code}.",
107
+ }
108
+ )
109
+ continue
110
+
111
+ token = response.json()["access_token"]
112
+
113
+ # Fetch alert details
114
+ alert_url = f"https://graph.microsoft.com/v1.0/security/alerts_v2/{hit.rule.id}"
115
+ response = requests.get(alert_url, headers={"Authorization": f"Bearer {token}"}, timeout=5.0)
116
+ if not response.ok:
117
+ logger.warning("GET request to Microsoft Graph failed with status code %s.", response.status_code)
118
+ report.append(
119
+ {
120
+ "query": query,
121
+ "outcome": "error",
122
+ "title": "Microsoft Graph API request failed",
123
+ "message": f"GET request to Microsoft Graph failed with status code {response.status_code}.",
124
+ }
125
+ )
126
+ continue
127
+ alert_data = response.json()
128
+
129
+ # Update alert
130
+ if (
131
+ "assessment" in hit.howler
132
+ and hit.howler.assessment in properties_map["graph"]["classification"]
133
+ and hit.howler.assessment in properties_map["graph"]["determination"]
134
+ ):
135
+ classification = properties_map["graph"]["classification"][hit.howler.assessment]
136
+ determination = properties_map["graph"]["determination"][hit.howler.assessment]
137
+ else:
138
+ classification = alert_data["classification"]
139
+ determination = alert_data["determination"]
140
+
141
+ status = properties_map["graph"]["status"][hit.howler.status]
142
+ assigned_to = alert_data["assignedTo"]
143
+
144
+ data = {
145
+ "assignedTo": assigned_to,
146
+ "classification": classification,
147
+ "determination": determination,
148
+ "status": status,
149
+ }
150
+
151
+ response = requests.patch(
152
+ alert_url,
153
+ json=data,
154
+ headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
155
+ timeout=5.0,
156
+ )
157
+ if not response.ok:
158
+ report.append(
159
+ {
160
+ "query": query,
161
+ "outcome": "error",
162
+ "title": "Microsoft Graph API request failed",
163
+ "message": f"PATCH request to Microsoft Graph failed with status code {response.status_code}.",
164
+ }
165
+ )
166
+
167
+ report.append(
168
+ {
169
+ "query": f"howler.id:{hit.howler.id}",
170
+ "outcome": "success",
171
+ "title": "Alert updated in XDR Defender",
172
+ "message": "Howler has successfuly propagated changes to this alert to XDR Defender.",
173
+ }
174
+ )
175
+
176
+ return report
177
+
178
+
179
+ def specification():
180
+ "Update Defender action specification"
181
+ return {
182
+ "id": OPERATION_ID,
183
+ "title": "Update Microsoft Defender XDR alert",
184
+ "priority": 8,
185
+ "description": {
186
+ "short": "Update Microsoft Defender XDR alert",
187
+ "long": execute.__doc__,
188
+ },
189
+ "roles": ["automation_basic"],
190
+ "steps": [{"args": {}, "options": {}, "validation": {}}],
191
+ "triggers": VALID_TRIGGERS,
192
+ }