check-msdefender 1.0.0__tar.gz → 1.1.1__tar.gz
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.
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/.claude/settings.local.json +2 -1
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/PKG-INFO +37 -2
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/README.md +36 -1
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender/cli/commands/__init__.py +2 -0
- check_msdefender-1.1.1/check_msdefender/cli/commands/alerts.py +61 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender/core/defender.py +38 -0
- check_msdefender-1.1.1/check_msdefender/services/alerts_service.py +98 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender.egg-info/PKG-INFO +37 -2
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender.egg-info/SOURCES.txt +6 -0
- check_msdefender-1.1.1/doc/Feat-MsDefender-Alerts.md +116 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/pyproject.toml +1 -1
- check_msdefender-1.1.1/tests/fixtures/alerts_data.json +72 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/tests/fixtures/machine_data.json +22 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/tests/fixtures/mock_defender_client.py +8 -0
- check_msdefender-1.1.1/tests/fixtures/test_alerts_service.py +150 -0
- check_msdefender-1.1.1/tests/unit/test_alerts_service.py +267 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/.env.example +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/.github/workflows/python-package.yml +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/.github/workflows/python-publish.yml +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/.gitignore +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/.idea/.gitignore +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/.idea/check_msdefender.iml +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/.idea/dictionaries/project.xml +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/.idea/encodings.xml +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/.idea/inspectionProfiles/profiles_settings.xml +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/.idea/misc.xml +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/.idea/modules.xml +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/.idea/runConfigurations/Integration_Tests.xml +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/.idea/vcs.xml +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/LICENSE +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender/__init__.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender/__main__.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender/check_msdefender.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender/cli/__init__.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender/cli/__main__.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender/cli/commands/detail.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender/cli/commands/lastseen.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender/cli/commands/machines.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender/cli/commands/onboarding.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender/cli/commands/vulnerabilities.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender/cli/decorators.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender/cli/handlers.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender/core/__init__.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender/core/auth.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender/core/config.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender/core/exceptions.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender/core/logging_config.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender/core/nagios.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender/services/__init__.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender/services/detail_service.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender/services/lastseen_service.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender/services/machines_service.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender/services/models.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender/services/onboarding_service.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender/services/vulnerabilities_service.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender.egg-info/dependency_links.txt +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender.egg-info/entry_points.txt +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender.egg-info/requires.txt +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender.egg-info/top_level.txt +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/check_msdefender.ini.example +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/doc/Feat-Click-Decorators-ErrorHandlers-Formatters.md +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/doc/Feat-Click-Groups.md +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/doc/Feat-Enhance-MsDefender-Vulnerabilities-Output.md +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/doc/Feat-Fixture-Tests.md +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/doc/Feat-Integration-Tests.md +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/doc/Feat-MsDefender-DetailMachine.md +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/doc/Feat-MsDefender-ListMachines.md +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/doc/Feat-MsDefender.md +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/doc/Feat-Nagios-Detailed-Output.md +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/doc/Feat-Nagios-Exit-Code.md +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/doc/Feat-Nagios-Output.md +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/doc/Feat-Pypi-Package.md +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/doc/Feat-Verbose.md +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/pytest.ini +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/requirements-dev.txt +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/requirements.txt +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/setup.cfg +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/test_verbose.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/tests/__init__.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/tests/fixtures/__init__.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/tests/fixtures/test_detail_service.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/tests/fixtures/test_lastseen_service.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/tests/fixtures/test_onboarding_service.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/tests/fixtures/test_vulnerabilities_service.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/tests/fixtures/vulnerability_data.json +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/tests/integration/__init__.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/tests/integration/test_cli_integration.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/tests/integration/test_lastseen_integration.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/tests/unit/__init__.py +0 -0
- {check_msdefender-1.0.0 → check_msdefender-1.1.1}/tests/unit/test_detail_service.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: check-msdefender
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.1
|
|
4
4
|
Summary: A Nagios plugin for monitoring Microsoft Defender API endpoints
|
|
5
5
|
Author-email: ldvchosal <ldvchosal@github.com>
|
|
6
6
|
License: MIT
|
|
@@ -46,7 +46,7 @@ A comprehensive **Nagios plugin** for monitoring Microsoft Defender for Endpoint
|
|
|
46
46
|
## ✨ Features
|
|
47
47
|
|
|
48
48
|
- 🔐 **Dual Authentication** - Support for Client Secret and Certificate-based authentication
|
|
49
|
-
- 🎯 **Multiple Endpoints** - Monitor onboarding status, last seen, vulnerabilities, and machine details
|
|
49
|
+
- 🎯 **Multiple Endpoints** - Monitor onboarding status, last seen, vulnerabilities, alerts, and machine details
|
|
50
50
|
- 📊 **Nagios Compatible** - Standard exit codes and performance data output
|
|
51
51
|
- 🏗️ **Clean Architecture** - Modular design with testable components
|
|
52
52
|
- 🔧 **Flexible Configuration** - File-based configuration with sensible defaults
|
|
@@ -78,6 +78,9 @@ check_msdefender lastseen -d machine.domain.tld -W 7 -C 30
|
|
|
78
78
|
# Check vulnerabilities
|
|
79
79
|
check_msdefender vulnerabilities -d machine.domain.tld -W 10 -C 100
|
|
80
80
|
|
|
81
|
+
# Check alerts
|
|
82
|
+
check_msdefender alerts -d machine.domain.tld -W 1 -C 5
|
|
83
|
+
|
|
81
84
|
# List all machines
|
|
82
85
|
check_msdefender machines
|
|
83
86
|
|
|
@@ -92,6 +95,7 @@ check_msdefender detail -d machine.domain.tld
|
|
|
92
95
|
| `onboarding` | Check machine onboarding status | W:1, C:2 |
|
|
93
96
|
| `lastseen` | Days since machine last seen | W:7, C:30 |
|
|
94
97
|
| `vulnerabilities` | Vulnerability score calculation | W:10, C:100 |
|
|
98
|
+
| `alerts` | Count of unresolved alerts | W:1, C:0 |
|
|
95
99
|
| `machines` | List all machines | W:10, C:25 |
|
|
96
100
|
| `detail` | Get detailed machine information | - |
|
|
97
101
|
|
|
@@ -103,6 +107,14 @@ The vulnerability score is calculated as:
|
|
|
103
107
|
- **Medium vulnerabilities** × 5
|
|
104
108
|
- **Low vulnerabilities** × 1
|
|
105
109
|
|
|
110
|
+
### Alert Monitoring
|
|
111
|
+
|
|
112
|
+
The alerts command monitors unresolved security alerts for a machine:
|
|
113
|
+
- **Counts only unresolved alerts** (status ≠ "Resolved")
|
|
114
|
+
- **Excludes informational alerts** when critical/warning alerts exist
|
|
115
|
+
- **Shows alert details** including creation time, title, and severity
|
|
116
|
+
- **Default thresholds**: Warning at 1 alert, Critical at 0 (meaning any alert triggers warning)
|
|
117
|
+
|
|
106
118
|
### Onboarding Status Values
|
|
107
119
|
|
|
108
120
|
- `0` - Onboarded ✅
|
|
@@ -145,6 +157,7 @@ timeout = 5
|
|
|
145
157
|
- `Machine.Read.All`
|
|
146
158
|
- `Vulnerability.Read`
|
|
147
159
|
- `Vulnerability.Read.All`
|
|
160
|
+
- `Alert.Read.All`
|
|
148
161
|
3. **Create Authentication** (Secret or Certificate)
|
|
149
162
|
4. **Note Credentials** (Client ID, Tenant ID, Secret/Certificate)
|
|
150
163
|
|
|
@@ -182,6 +195,11 @@ define command {
|
|
|
182
195
|
command_name check_defender_vulnerabilities
|
|
183
196
|
command_line $USER1$/check_msdefender/bin/check_msdefender vulnerabilities -d $HOSTALIAS$ -W 10 -C 100
|
|
184
197
|
}
|
|
198
|
+
|
|
199
|
+
define command {
|
|
200
|
+
command_name check_defender_alerts
|
|
201
|
+
command_line $USER1$/check_msdefender/bin/check_msdefender alerts -d $HOSTALIAS$ -W 1 -C 5
|
|
202
|
+
}
|
|
185
203
|
```
|
|
186
204
|
|
|
187
205
|
### Service Definitions
|
|
@@ -208,6 +226,13 @@ define service {
|
|
|
208
226
|
check_command check_defender_vulnerabilities
|
|
209
227
|
hostgroup_name msdefender
|
|
210
228
|
}
|
|
229
|
+
|
|
230
|
+
define service {
|
|
231
|
+
use generic-service
|
|
232
|
+
service_description DEFENDER_ALERTS
|
|
233
|
+
check_command check_defender_alerts
|
|
234
|
+
hostgroup_name msdefender
|
|
235
|
+
}
|
|
211
236
|
```
|
|
212
237
|
|
|
213
238
|
## 🏗️ Architecture
|
|
@@ -221,6 +246,7 @@ check_msdefender/
|
|
|
221
246
|
│ │ ├── onboarding.py # Onboarding status command
|
|
222
247
|
│ │ ├── lastseen.py # Last seen command
|
|
223
248
|
│ │ ├── vulnerabilities.py # Vulnerabilities command
|
|
249
|
+
│ │ ├── alerts.py # Alerts monitoring command
|
|
224
250
|
│ │ ├── machines.py # List machines command
|
|
225
251
|
│ │ └── detail.py # Machine detail command
|
|
226
252
|
│ ├── decorators.py # Common CLI decorators
|
|
@@ -236,6 +262,7 @@ check_msdefender/
|
|
|
236
262
|
│ ├── onboarding_service.py # Onboarding business logic
|
|
237
263
|
│ ├── lastseen_service.py # Last seen business logic
|
|
238
264
|
│ ├── vulnerabilities_service.py # Vulnerability business logic
|
|
265
|
+
│ ├── alerts_service.py # Alerts monitoring business logic
|
|
239
266
|
│ ├── machines_service.py # Machines business logic
|
|
240
267
|
│ ├── detail_service.py # Detail business logic
|
|
241
268
|
│ └── models.py # Data models
|
|
@@ -317,6 +344,14 @@ DEFENDER WARNING - Last seen: 10 days ago | lastseen=10;7;30;0;
|
|
|
317
344
|
DEFENDER CRITICAL - Vulnerability score: 150 (1 Critical, 5 High) | vulnerabilities=150;10;100;0;
|
|
318
345
|
```
|
|
319
346
|
|
|
347
|
+
### Alerts Warning
|
|
348
|
+
```
|
|
349
|
+
DEFENDER WARNING - Unresolved alerts for machine.domain.com | alerts=2;1;5;0;
|
|
350
|
+
Unresolved alerts for machine.domain.com
|
|
351
|
+
2025-09-14T10:22:14.12Z - Suspicious activity detected (New high)
|
|
352
|
+
2025-09-14T12:00:00.00Z - Malware detection (InProgress medium)
|
|
353
|
+
```
|
|
354
|
+
|
|
320
355
|
## 🔧 Troubleshooting
|
|
321
356
|
|
|
322
357
|
### Common Issues
|
|
@@ -9,7 +9,7 @@ A comprehensive **Nagios plugin** for monitoring Microsoft Defender for Endpoint
|
|
|
9
9
|
## ✨ Features
|
|
10
10
|
|
|
11
11
|
- 🔐 **Dual Authentication** - Support for Client Secret and Certificate-based authentication
|
|
12
|
-
- 🎯 **Multiple Endpoints** - Monitor onboarding status, last seen, vulnerabilities, and machine details
|
|
12
|
+
- 🎯 **Multiple Endpoints** - Monitor onboarding status, last seen, vulnerabilities, alerts, and machine details
|
|
13
13
|
- 📊 **Nagios Compatible** - Standard exit codes and performance data output
|
|
14
14
|
- 🏗️ **Clean Architecture** - Modular design with testable components
|
|
15
15
|
- 🔧 **Flexible Configuration** - File-based configuration with sensible defaults
|
|
@@ -41,6 +41,9 @@ check_msdefender lastseen -d machine.domain.tld -W 7 -C 30
|
|
|
41
41
|
# Check vulnerabilities
|
|
42
42
|
check_msdefender vulnerabilities -d machine.domain.tld -W 10 -C 100
|
|
43
43
|
|
|
44
|
+
# Check alerts
|
|
45
|
+
check_msdefender alerts -d machine.domain.tld -W 1 -C 5
|
|
46
|
+
|
|
44
47
|
# List all machines
|
|
45
48
|
check_msdefender machines
|
|
46
49
|
|
|
@@ -55,6 +58,7 @@ check_msdefender detail -d machine.domain.tld
|
|
|
55
58
|
| `onboarding` | Check machine onboarding status | W:1, C:2 |
|
|
56
59
|
| `lastseen` | Days since machine last seen | W:7, C:30 |
|
|
57
60
|
| `vulnerabilities` | Vulnerability score calculation | W:10, C:100 |
|
|
61
|
+
| `alerts` | Count of unresolved alerts | W:1, C:0 |
|
|
58
62
|
| `machines` | List all machines | W:10, C:25 |
|
|
59
63
|
| `detail` | Get detailed machine information | - |
|
|
60
64
|
|
|
@@ -66,6 +70,14 @@ The vulnerability score is calculated as:
|
|
|
66
70
|
- **Medium vulnerabilities** × 5
|
|
67
71
|
- **Low vulnerabilities** × 1
|
|
68
72
|
|
|
73
|
+
### Alert Monitoring
|
|
74
|
+
|
|
75
|
+
The alerts command monitors unresolved security alerts for a machine:
|
|
76
|
+
- **Counts only unresolved alerts** (status ≠ "Resolved")
|
|
77
|
+
- **Excludes informational alerts** when critical/warning alerts exist
|
|
78
|
+
- **Shows alert details** including creation time, title, and severity
|
|
79
|
+
- **Default thresholds**: Warning at 1 alert, Critical at 0 (meaning any alert triggers warning)
|
|
80
|
+
|
|
69
81
|
### Onboarding Status Values
|
|
70
82
|
|
|
71
83
|
- `0` - Onboarded ✅
|
|
@@ -108,6 +120,7 @@ timeout = 5
|
|
|
108
120
|
- `Machine.Read.All`
|
|
109
121
|
- `Vulnerability.Read`
|
|
110
122
|
- `Vulnerability.Read.All`
|
|
123
|
+
- `Alert.Read.All`
|
|
111
124
|
3. **Create Authentication** (Secret or Certificate)
|
|
112
125
|
4. **Note Credentials** (Client ID, Tenant ID, Secret/Certificate)
|
|
113
126
|
|
|
@@ -145,6 +158,11 @@ define command {
|
|
|
145
158
|
command_name check_defender_vulnerabilities
|
|
146
159
|
command_line $USER1$/check_msdefender/bin/check_msdefender vulnerabilities -d $HOSTALIAS$ -W 10 -C 100
|
|
147
160
|
}
|
|
161
|
+
|
|
162
|
+
define command {
|
|
163
|
+
command_name check_defender_alerts
|
|
164
|
+
command_line $USER1$/check_msdefender/bin/check_msdefender alerts -d $HOSTALIAS$ -W 1 -C 5
|
|
165
|
+
}
|
|
148
166
|
```
|
|
149
167
|
|
|
150
168
|
### Service Definitions
|
|
@@ -171,6 +189,13 @@ define service {
|
|
|
171
189
|
check_command check_defender_vulnerabilities
|
|
172
190
|
hostgroup_name msdefender
|
|
173
191
|
}
|
|
192
|
+
|
|
193
|
+
define service {
|
|
194
|
+
use generic-service
|
|
195
|
+
service_description DEFENDER_ALERTS
|
|
196
|
+
check_command check_defender_alerts
|
|
197
|
+
hostgroup_name msdefender
|
|
198
|
+
}
|
|
174
199
|
```
|
|
175
200
|
|
|
176
201
|
## 🏗️ Architecture
|
|
@@ -184,6 +209,7 @@ check_msdefender/
|
|
|
184
209
|
│ │ ├── onboarding.py # Onboarding status command
|
|
185
210
|
│ │ ├── lastseen.py # Last seen command
|
|
186
211
|
│ │ ├── vulnerabilities.py # Vulnerabilities command
|
|
212
|
+
│ │ ├── alerts.py # Alerts monitoring command
|
|
187
213
|
│ │ ├── machines.py # List machines command
|
|
188
214
|
│ │ └── detail.py # Machine detail command
|
|
189
215
|
│ ├── decorators.py # Common CLI decorators
|
|
@@ -199,6 +225,7 @@ check_msdefender/
|
|
|
199
225
|
│ ├── onboarding_service.py # Onboarding business logic
|
|
200
226
|
│ ├── lastseen_service.py # Last seen business logic
|
|
201
227
|
│ ├── vulnerabilities_service.py # Vulnerability business logic
|
|
228
|
+
│ ├── alerts_service.py # Alerts monitoring business logic
|
|
202
229
|
│ ├── machines_service.py # Machines business logic
|
|
203
230
|
│ ├── detail_service.py # Detail business logic
|
|
204
231
|
│ └── models.py # Data models
|
|
@@ -280,6 +307,14 @@ DEFENDER WARNING - Last seen: 10 days ago | lastseen=10;7;30;0;
|
|
|
280
307
|
DEFENDER CRITICAL - Vulnerability score: 150 (1 Critical, 5 High) | vulnerabilities=150;10;100;0;
|
|
281
308
|
```
|
|
282
309
|
|
|
310
|
+
### Alerts Warning
|
|
311
|
+
```
|
|
312
|
+
DEFENDER WARNING - Unresolved alerts for machine.domain.com | alerts=2;1;5;0;
|
|
313
|
+
Unresolved alerts for machine.domain.com
|
|
314
|
+
2025-09-14T10:22:14.12Z - Suspicious activity detected (New high)
|
|
315
|
+
2025-09-14T12:00:00.00Z - Malware detection (InProgress medium)
|
|
316
|
+
```
|
|
317
|
+
|
|
283
318
|
## 🔧 Troubleshooting
|
|
284
319
|
|
|
285
320
|
### Common Issues
|
|
@@ -6,6 +6,7 @@ from .vulnerabilities import register_vulnerability_commands
|
|
|
6
6
|
from .onboarding import register_onboarding_commands
|
|
7
7
|
from .machines import register_machines_commands
|
|
8
8
|
from .detail import register_detail_commands
|
|
9
|
+
from .alerts import register_alerts_commands
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
def register_all_commands(main_group: Any) -> None:
|
|
@@ -15,3 +16,4 @@ def register_all_commands(main_group: Any) -> None:
|
|
|
15
16
|
register_onboarding_commands(main_group)
|
|
16
17
|
register_machines_commands(main_group)
|
|
17
18
|
register_detail_commands(main_group)
|
|
19
|
+
register_alerts_commands(main_group)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Alerts commands for CLI."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
import click
|
|
5
|
+
from typing import Optional, Any
|
|
6
|
+
|
|
7
|
+
from check_msdefender.core.auth import get_authenticator
|
|
8
|
+
from check_msdefender.core.config import load_config
|
|
9
|
+
from check_msdefender.core.defender import DefenderClient
|
|
10
|
+
from check_msdefender.core.nagios import NagiosPlugin
|
|
11
|
+
from check_msdefender.services.alerts_service import AlertsService
|
|
12
|
+
from ..decorators import common_options
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def register_alerts_commands(main_group: Any) -> None:
|
|
16
|
+
"""Register alerts commands with the main CLI group."""
|
|
17
|
+
|
|
18
|
+
@main_group.command("alerts")
|
|
19
|
+
@common_options
|
|
20
|
+
def alerts_cmd(
|
|
21
|
+
config: str,
|
|
22
|
+
verbose: int,
|
|
23
|
+
machine_id: Optional[str],
|
|
24
|
+
dns_name: Optional[str],
|
|
25
|
+
warning: Optional[float],
|
|
26
|
+
critical: Optional[float],
|
|
27
|
+
) -> None:
|
|
28
|
+
"""Check alerts for Microsoft Defender."""
|
|
29
|
+
warning = warning if warning is not None else 1
|
|
30
|
+
critical = critical if critical is not None else 0
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
# Load configuration
|
|
34
|
+
cfg = load_config(config)
|
|
35
|
+
|
|
36
|
+
# Get authenticator
|
|
37
|
+
authenticator = get_authenticator(cfg)
|
|
38
|
+
|
|
39
|
+
# Create Defender client
|
|
40
|
+
client = DefenderClient(authenticator, verbose_level=verbose)
|
|
41
|
+
|
|
42
|
+
# Create the alerts service
|
|
43
|
+
service = AlertsService(client, verbose_level=verbose)
|
|
44
|
+
|
|
45
|
+
# Create Nagios plugin
|
|
46
|
+
plugin = NagiosPlugin(service, "alerts")
|
|
47
|
+
|
|
48
|
+
# Execute check
|
|
49
|
+
result = plugin.check(
|
|
50
|
+
machine_id=machine_id,
|
|
51
|
+
dns_name=dns_name,
|
|
52
|
+
warning=warning,
|
|
53
|
+
critical=critical,
|
|
54
|
+
verbose=verbose,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
sys.exit(result or 0)
|
|
58
|
+
|
|
59
|
+
except Exception as e:
|
|
60
|
+
print(f"UNKNOWN: {str(e)}")
|
|
61
|
+
sys.exit(3)
|
|
@@ -167,6 +167,44 @@ class DefenderClient:
|
|
|
167
167
|
self.logger.debug(f"Response: {str(e.response.content)}")
|
|
168
168
|
raise DefenderAPIError(f"Failed to query MS Defender API: {str(e)}")
|
|
169
169
|
|
|
170
|
+
def get_alerts(self) -> Dict[str, Any]:
|
|
171
|
+
"""Get alerts from Microsoft Defender."""
|
|
172
|
+
self.logger.method_entry("get_alerts")
|
|
173
|
+
|
|
174
|
+
token = self._get_token()
|
|
175
|
+
|
|
176
|
+
url = f"{self.base_url}/api/alerts"
|
|
177
|
+
headers = {
|
|
178
|
+
"Authorization": f"Bearer {token}",
|
|
179
|
+
"Content-Type": DefenderClient.application_json,
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
params = {
|
|
183
|
+
"$top": "100",
|
|
184
|
+
"$expand": "evidence",
|
|
185
|
+
"$orderby": "alertCreationTime desc",
|
|
186
|
+
"$select": "status,title,machineId,computerDnsName,alertCreationTime,firstEventTime,lastEventTime,lastUpdateTime,severity",
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
try:
|
|
190
|
+
start_time = time.time()
|
|
191
|
+
self.logger.info("Querying alerts")
|
|
192
|
+
response = requests.get(url, headers=headers, params=params, timeout=self.timeout)
|
|
193
|
+
elapsed = time.time() - start_time
|
|
194
|
+
|
|
195
|
+
self.logger.api_call("GET", url, response.status_code, elapsed)
|
|
196
|
+
response.raise_for_status()
|
|
197
|
+
|
|
198
|
+
result = cast(Dict[str, Any], response.json())
|
|
199
|
+
self.logger.json_response(str(result))
|
|
200
|
+
self.logger.method_exit("get_alerts", result)
|
|
201
|
+
return result
|
|
202
|
+
except requests.RequestException as e:
|
|
203
|
+
self.logger.debug(f"API request failed: {str(e)}")
|
|
204
|
+
if hasattr(e, "response") and e.response is not None:
|
|
205
|
+
self.logger.debug(f"Response: {str(e.response.content)}")
|
|
206
|
+
raise DefenderAPIError(f"Failed to query MS Defender API: {str(e)}")
|
|
207
|
+
|
|
170
208
|
def _get_token(self) -> str:
|
|
171
209
|
"""Get access token from authenticator."""
|
|
172
210
|
self.logger.trace("Getting access token from authenticator")
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""Alerts service implementation."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import Dict, Optional, Any, List
|
|
5
|
+
from check_msdefender.core.exceptions import ValidationError
|
|
6
|
+
from check_msdefender.core.logging_config import get_verbose_logger
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AlertsService:
|
|
10
|
+
"""Service for checking machine alerts."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, defender_client: Any, verbose_level: int = 0) -> None:
|
|
13
|
+
"""Initialize with Defender client."""
|
|
14
|
+
self.defender = defender_client
|
|
15
|
+
self.logger = get_verbose_logger(__name__, verbose_level)
|
|
16
|
+
|
|
17
|
+
def get_result(
|
|
18
|
+
self, machine_id: Optional[str] = None, dns_name: Optional[str] = None
|
|
19
|
+
) -> Dict[str, Any]:
|
|
20
|
+
"""Get alerts result with value and details for a machine."""
|
|
21
|
+
self.logger.method_entry("get_result", machine_id=machine_id, dns_name=dns_name)
|
|
22
|
+
|
|
23
|
+
if not machine_id and not dns_name:
|
|
24
|
+
raise ValidationError("Either machine_id or dns_name must be provided")
|
|
25
|
+
|
|
26
|
+
# Get machine information
|
|
27
|
+
target_dns_name = dns_name
|
|
28
|
+
target_machine_id = machine_id
|
|
29
|
+
|
|
30
|
+
if machine_id:
|
|
31
|
+
# Get DNS name from machine_id
|
|
32
|
+
machine_details = self.defender.get_machine_by_id(machine_id)
|
|
33
|
+
target_dns_name = machine_details.get("computerDnsName", "Unknown")
|
|
34
|
+
elif dns_name:
|
|
35
|
+
# Get machine_id from dns_name
|
|
36
|
+
dns_response = self.defender.get_machine_by_dns_name(dns_name)
|
|
37
|
+
machines = dns_response.get("value", [])
|
|
38
|
+
if not machines:
|
|
39
|
+
raise ValidationError(f"Machine not found with DNS name: {dns_name}")
|
|
40
|
+
target_machine_id = machines[0].get("id")
|
|
41
|
+
target_dns_name = dns_name
|
|
42
|
+
|
|
43
|
+
# Get all alerts
|
|
44
|
+
self.logger.info("Fetching alerts from Microsoft Defender")
|
|
45
|
+
alerts_data = self.defender.get_alerts()
|
|
46
|
+
all_alerts = alerts_data.get("value", [])
|
|
47
|
+
|
|
48
|
+
# Filter alerts for the specific machine
|
|
49
|
+
machine_alerts = [
|
|
50
|
+
alert
|
|
51
|
+
for alert in all_alerts
|
|
52
|
+
if alert.get("machineId") == target_machine_id
|
|
53
|
+
or alert.get("computerDnsName") == target_dns_name
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
self.logger.info(f"Found {len(machine_alerts)} alerts for machine {target_dns_name}")
|
|
57
|
+
|
|
58
|
+
# Categorize alerts by status and severity
|
|
59
|
+
unresolved_alerts = [alert for alert in machine_alerts if alert.get("status") != "Resolved"]
|
|
60
|
+
informational_alerts = [
|
|
61
|
+
alert for alert in unresolved_alerts if alert.get("severity") == "Informational"
|
|
62
|
+
]
|
|
63
|
+
critical_warning_alerts = [
|
|
64
|
+
alert
|
|
65
|
+
for alert in unresolved_alerts
|
|
66
|
+
if alert.get("severity") in ["High", "Medium", "Low"]
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
# Create details for output
|
|
70
|
+
details = []
|
|
71
|
+
if unresolved_alerts:
|
|
72
|
+
summary_line = f"Unresolved alerts for {target_dns_name}"
|
|
73
|
+
if informational_alerts and not critical_warning_alerts:
|
|
74
|
+
summary_line = f"Unresolved informational alerts for {target_dns_name}"
|
|
75
|
+
elif critical_warning_alerts:
|
|
76
|
+
summary_line = f"Unresolved alerts for {target_dns_name}"
|
|
77
|
+
details.append(summary_line)
|
|
78
|
+
|
|
79
|
+
# Add individual alerts (limit to 10)
|
|
80
|
+
for alert in unresolved_alerts[:10]:
|
|
81
|
+
creation_time = alert.get("alertCreationTime", "Unknown")
|
|
82
|
+
title = alert.get("title", "Unknown alert")
|
|
83
|
+
status = alert.get("status", "Unknown")
|
|
84
|
+
severity = alert.get("severity", "Unknown")
|
|
85
|
+
details.append(f"{creation_time} - {title} ({status} {severity.lower()})")
|
|
86
|
+
|
|
87
|
+
# Return the number of unresolved alerts as the value
|
|
88
|
+
# This will be used by Nagios plugin for determining status based on thresholds
|
|
89
|
+
value = len(unresolved_alerts)
|
|
90
|
+
|
|
91
|
+
result = {
|
|
92
|
+
"value": value,
|
|
93
|
+
"details": details,
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
self.logger.info(f"Alert analysis complete: {len(unresolved_alerts)} unresolved alerts")
|
|
97
|
+
self.logger.method_exit("get_result", result)
|
|
98
|
+
return result
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: check-msdefender
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.1
|
|
4
4
|
Summary: A Nagios plugin for monitoring Microsoft Defender API endpoints
|
|
5
5
|
Author-email: ldvchosal <ldvchosal@github.com>
|
|
6
6
|
License: MIT
|
|
@@ -46,7 +46,7 @@ A comprehensive **Nagios plugin** for monitoring Microsoft Defender for Endpoint
|
|
|
46
46
|
## ✨ Features
|
|
47
47
|
|
|
48
48
|
- 🔐 **Dual Authentication** - Support for Client Secret and Certificate-based authentication
|
|
49
|
-
- 🎯 **Multiple Endpoints** - Monitor onboarding status, last seen, vulnerabilities, and machine details
|
|
49
|
+
- 🎯 **Multiple Endpoints** - Monitor onboarding status, last seen, vulnerabilities, alerts, and machine details
|
|
50
50
|
- 📊 **Nagios Compatible** - Standard exit codes and performance data output
|
|
51
51
|
- 🏗️ **Clean Architecture** - Modular design with testable components
|
|
52
52
|
- 🔧 **Flexible Configuration** - File-based configuration with sensible defaults
|
|
@@ -78,6 +78,9 @@ check_msdefender lastseen -d machine.domain.tld -W 7 -C 30
|
|
|
78
78
|
# Check vulnerabilities
|
|
79
79
|
check_msdefender vulnerabilities -d machine.domain.tld -W 10 -C 100
|
|
80
80
|
|
|
81
|
+
# Check alerts
|
|
82
|
+
check_msdefender alerts -d machine.domain.tld -W 1 -C 5
|
|
83
|
+
|
|
81
84
|
# List all machines
|
|
82
85
|
check_msdefender machines
|
|
83
86
|
|
|
@@ -92,6 +95,7 @@ check_msdefender detail -d machine.domain.tld
|
|
|
92
95
|
| `onboarding` | Check machine onboarding status | W:1, C:2 |
|
|
93
96
|
| `lastseen` | Days since machine last seen | W:7, C:30 |
|
|
94
97
|
| `vulnerabilities` | Vulnerability score calculation | W:10, C:100 |
|
|
98
|
+
| `alerts` | Count of unresolved alerts | W:1, C:0 |
|
|
95
99
|
| `machines` | List all machines | W:10, C:25 |
|
|
96
100
|
| `detail` | Get detailed machine information | - |
|
|
97
101
|
|
|
@@ -103,6 +107,14 @@ The vulnerability score is calculated as:
|
|
|
103
107
|
- **Medium vulnerabilities** × 5
|
|
104
108
|
- **Low vulnerabilities** × 1
|
|
105
109
|
|
|
110
|
+
### Alert Monitoring
|
|
111
|
+
|
|
112
|
+
The alerts command monitors unresolved security alerts for a machine:
|
|
113
|
+
- **Counts only unresolved alerts** (status ≠ "Resolved")
|
|
114
|
+
- **Excludes informational alerts** when critical/warning alerts exist
|
|
115
|
+
- **Shows alert details** including creation time, title, and severity
|
|
116
|
+
- **Default thresholds**: Warning at 1 alert, Critical at 0 (meaning any alert triggers warning)
|
|
117
|
+
|
|
106
118
|
### Onboarding Status Values
|
|
107
119
|
|
|
108
120
|
- `0` - Onboarded ✅
|
|
@@ -145,6 +157,7 @@ timeout = 5
|
|
|
145
157
|
- `Machine.Read.All`
|
|
146
158
|
- `Vulnerability.Read`
|
|
147
159
|
- `Vulnerability.Read.All`
|
|
160
|
+
- `Alert.Read.All`
|
|
148
161
|
3. **Create Authentication** (Secret or Certificate)
|
|
149
162
|
4. **Note Credentials** (Client ID, Tenant ID, Secret/Certificate)
|
|
150
163
|
|
|
@@ -182,6 +195,11 @@ define command {
|
|
|
182
195
|
command_name check_defender_vulnerabilities
|
|
183
196
|
command_line $USER1$/check_msdefender/bin/check_msdefender vulnerabilities -d $HOSTALIAS$ -W 10 -C 100
|
|
184
197
|
}
|
|
198
|
+
|
|
199
|
+
define command {
|
|
200
|
+
command_name check_defender_alerts
|
|
201
|
+
command_line $USER1$/check_msdefender/bin/check_msdefender alerts -d $HOSTALIAS$ -W 1 -C 5
|
|
202
|
+
}
|
|
185
203
|
```
|
|
186
204
|
|
|
187
205
|
### Service Definitions
|
|
@@ -208,6 +226,13 @@ define service {
|
|
|
208
226
|
check_command check_defender_vulnerabilities
|
|
209
227
|
hostgroup_name msdefender
|
|
210
228
|
}
|
|
229
|
+
|
|
230
|
+
define service {
|
|
231
|
+
use generic-service
|
|
232
|
+
service_description DEFENDER_ALERTS
|
|
233
|
+
check_command check_defender_alerts
|
|
234
|
+
hostgroup_name msdefender
|
|
235
|
+
}
|
|
211
236
|
```
|
|
212
237
|
|
|
213
238
|
## 🏗️ Architecture
|
|
@@ -221,6 +246,7 @@ check_msdefender/
|
|
|
221
246
|
│ │ ├── onboarding.py # Onboarding status command
|
|
222
247
|
│ │ ├── lastseen.py # Last seen command
|
|
223
248
|
│ │ ├── vulnerabilities.py # Vulnerabilities command
|
|
249
|
+
│ │ ├── alerts.py # Alerts monitoring command
|
|
224
250
|
│ │ ├── machines.py # List machines command
|
|
225
251
|
│ │ └── detail.py # Machine detail command
|
|
226
252
|
│ ├── decorators.py # Common CLI decorators
|
|
@@ -236,6 +262,7 @@ check_msdefender/
|
|
|
236
262
|
│ ├── onboarding_service.py # Onboarding business logic
|
|
237
263
|
│ ├── lastseen_service.py # Last seen business logic
|
|
238
264
|
│ ├── vulnerabilities_service.py # Vulnerability business logic
|
|
265
|
+
│ ├── alerts_service.py # Alerts monitoring business logic
|
|
239
266
|
│ ├── machines_service.py # Machines business logic
|
|
240
267
|
│ ├── detail_service.py # Detail business logic
|
|
241
268
|
│ └── models.py # Data models
|
|
@@ -317,6 +344,14 @@ DEFENDER WARNING - Last seen: 10 days ago | lastseen=10;7;30;0;
|
|
|
317
344
|
DEFENDER CRITICAL - Vulnerability score: 150 (1 Critical, 5 High) | vulnerabilities=150;10;100;0;
|
|
318
345
|
```
|
|
319
346
|
|
|
347
|
+
### Alerts Warning
|
|
348
|
+
```
|
|
349
|
+
DEFENDER WARNING - Unresolved alerts for machine.domain.com | alerts=2;1;5;0;
|
|
350
|
+
Unresolved alerts for machine.domain.com
|
|
351
|
+
2025-09-14T10:22:14.12Z - Suspicious activity detected (New high)
|
|
352
|
+
2025-09-14T12:00:00.00Z - Malware detection (InProgress medium)
|
|
353
|
+
```
|
|
354
|
+
|
|
320
355
|
## 🔧 Troubleshooting
|
|
321
356
|
|
|
322
357
|
### Common Issues
|
|
@@ -34,6 +34,7 @@ check_msdefender/cli/__main__.py
|
|
|
34
34
|
check_msdefender/cli/decorators.py
|
|
35
35
|
check_msdefender/cli/handlers.py
|
|
36
36
|
check_msdefender/cli/commands/__init__.py
|
|
37
|
+
check_msdefender/cli/commands/alerts.py
|
|
37
38
|
check_msdefender/cli/commands/detail.py
|
|
38
39
|
check_msdefender/cli/commands/lastseen.py
|
|
39
40
|
check_msdefender/cli/commands/machines.py
|
|
@@ -47,6 +48,7 @@ check_msdefender/core/exceptions.py
|
|
|
47
48
|
check_msdefender/core/logging_config.py
|
|
48
49
|
check_msdefender/core/nagios.py
|
|
49
50
|
check_msdefender/services/__init__.py
|
|
51
|
+
check_msdefender/services/alerts_service.py
|
|
50
52
|
check_msdefender/services/detail_service.py
|
|
51
53
|
check_msdefender/services/lastseen_service.py
|
|
52
54
|
check_msdefender/services/machines_service.py
|
|
@@ -58,6 +60,7 @@ doc/Feat-Click-Groups.md
|
|
|
58
60
|
doc/Feat-Enhance-MsDefender-Vulnerabilities-Output.md
|
|
59
61
|
doc/Feat-Fixture-Tests.md
|
|
60
62
|
doc/Feat-Integration-Tests.md
|
|
63
|
+
doc/Feat-MsDefender-Alerts.md
|
|
61
64
|
doc/Feat-MsDefender-DetailMachine.md
|
|
62
65
|
doc/Feat-MsDefender-ListMachines.md
|
|
63
66
|
doc/Feat-MsDefender.md
|
|
@@ -68,8 +71,10 @@ doc/Feat-Pypi-Package.md
|
|
|
68
71
|
doc/Feat-Verbose.md
|
|
69
72
|
tests/__init__.py
|
|
70
73
|
tests/fixtures/__init__.py
|
|
74
|
+
tests/fixtures/alerts_data.json
|
|
71
75
|
tests/fixtures/machine_data.json
|
|
72
76
|
tests/fixtures/mock_defender_client.py
|
|
77
|
+
tests/fixtures/test_alerts_service.py
|
|
73
78
|
tests/fixtures/test_detail_service.py
|
|
74
79
|
tests/fixtures/test_lastseen_service.py
|
|
75
80
|
tests/fixtures/test_onboarding_service.py
|
|
@@ -79,4 +84,5 @@ tests/integration/__init__.py
|
|
|
79
84
|
tests/integration/test_cli_integration.py
|
|
80
85
|
tests/integration/test_lastseen_integration.py
|
|
81
86
|
tests/unit/__init__.py
|
|
87
|
+
tests/unit/test_alerts_service.py
|
|
82
88
|
tests/unit/test_detail_service.py
|