check-msdefender 1.1.13__tar.gz → 1.1.15__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.
Files changed (52) hide show
  1. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/PKG-INFO +5 -4
  2. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/README.md +1 -1
  3. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/check_msdefender/__init__.py +1 -1
  4. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/check_msdefender/core/nagios.py +1 -4
  5. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/check_msdefender/services/products_service.py +30 -17
  6. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/pyproject.toml +5 -4
  7. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/tests/fixtures/test_detail_service.py +2 -0
  8. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/LICENSE +0 -0
  9. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/check_msdefender/__main__.py +0 -0
  10. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/check_msdefender/check_msdefender.py +0 -0
  11. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/check_msdefender/cli/__init__.py +0 -0
  12. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/check_msdefender/cli/__main__.py +0 -0
  13. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/check_msdefender/cli/commands/__init__.py +0 -0
  14. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/check_msdefender/cli/commands/alerts.py +0 -0
  15. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/check_msdefender/cli/commands/detail.py +0 -0
  16. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/check_msdefender/cli/commands/lastseen.py +0 -0
  17. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/check_msdefender/cli/commands/machines.py +0 -0
  18. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/check_msdefender/cli/commands/onboarding.py +0 -0
  19. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/check_msdefender/cli/commands/products.py +0 -0
  20. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/check_msdefender/cli/commands/vulnerabilities.py +0 -0
  21. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/check_msdefender/cli/decorators.py +0 -0
  22. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/check_msdefender/cli/handlers.py +0 -0
  23. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/check_msdefender/core/__init__.py +0 -0
  24. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/check_msdefender/core/auth.py +0 -0
  25. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/check_msdefender/core/config.py +0 -0
  26. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/check_msdefender/core/defender.py +0 -0
  27. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/check_msdefender/core/exceptions.py +0 -0
  28. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/check_msdefender/core/logging_config.py +0 -0
  29. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/check_msdefender/services/__init__.py +0 -0
  30. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/check_msdefender/services/alerts_service.py +0 -0
  31. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/check_msdefender/services/detail_service.py +0 -0
  32. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/check_msdefender/services/lastseen_service.py +0 -0
  33. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/check_msdefender/services/machines_service.py +0 -0
  34. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/check_msdefender/services/models.py +0 -0
  35. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/check_msdefender/services/onboarding_service.py +0 -0
  36. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/check_msdefender/services/vulnerabilities_service.py +0 -0
  37. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/tests/__init__.py +0 -0
  38. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/tests/fixtures/__init__.py +0 -0
  39. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/tests/fixtures/alerts_data.json +0 -0
  40. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/tests/fixtures/machine_data.json +0 -0
  41. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/tests/fixtures/mock_defender_client.py +0 -0
  42. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/tests/fixtures/test_alerts_service.py +0 -0
  43. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/tests/fixtures/test_lastseen_service.py +0 -0
  44. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/tests/fixtures/test_onboarding_service.py +0 -0
  45. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/tests/fixtures/test_vulnerabilities_service.py +0 -0
  46. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/tests/fixtures/vulnerability_data.json +0 -0
  47. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/tests/integration/__init__.py +0 -0
  48. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/tests/integration/test_cli_integration.py +0 -0
  49. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/tests/integration/test_lastseen_integration.py +0 -0
  50. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/tests/unit/__init__.py +0 -0
  51. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/tests/unit/test_alerts_service.py +0 -0
  52. {check_msdefender-1.1.13 → check_msdefender-1.1.15}/tests/unit/test_detail_service.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: check-msdefender
3
- Version: 1.1.13
3
+ Version: 1.1.15
4
4
  Summary: A Nagios plugin for monitoring Microsoft Defender API endpoints
5
5
  Keywords: nagios,monitoring,microsoft,graph,api,azure
6
6
  Author-Email: ldvchosal <ldvchosal@github.com>
@@ -10,16 +10,17 @@ Classifier: Intended Audience :: System Administrators
10
10
  Classifier: License :: OSI Approved :: MIT License
11
11
  Classifier: Operating System :: OS Independent
12
12
  Classifier: Programming Language :: Python :: 3
13
- Classifier: Programming Language :: Python :: 3.9
14
13
  Classifier: Programming Language :: Python :: 3.10
15
14
  Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
16
17
  Classifier: Topic :: System :: Monitoring
17
18
  Classifier: Topic :: System :: Systems Administration
18
19
  Project-URL: Homepage, https://github.com/lduchosal/check_msdefender
19
20
  Project-URL: Bug Reports, https://github.com/lduchosal/check_msdefender/issues
20
21
  Project-URL: Source, https://github.com/lduchosal/check_msdefender
21
22
  Project-URL: Documentation, https://github.com/lduchosal/check_msdefender/blob/main/README.md
22
- Requires-Python: >=3.9
23
+ Requires-Python: >=3.10
23
24
  Requires-Dist: nagiosplugin>=1.4.0
24
25
  Requires-Dist: azure-identity>=1.12.0
25
26
  Requires-Dist: click<9.0,>=8.0
@@ -27,7 +28,7 @@ Description-Content-Type: text/markdown
27
28
 
28
29
  # 🛡️ Check MS Defender
29
30
 
30
- [![Python Version](https://img.shields.io/badge/python-3.9+-blue.svg)](https://python.org)
31
+ [![Python Version](https://img.shields.io/badge/python-3.10+-blue.svg)](https://python.org)
31
32
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
32
33
  [![Build Status](https://img.shields.io/badge/build-passing-brightgreen.svg)](https://github.com/lduchosal/check_msdefender)
33
34
 
@@ -1,6 +1,6 @@
1
1
  # 🛡️ Check MS Defender
2
2
 
3
- [![Python Version](https://img.shields.io/badge/python-3.9+-blue.svg)](https://python.org)
3
+ [![Python Version](https://img.shields.io/badge/python-3.10+-blue.svg)](https://python.org)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
  [![Build Status](https://img.shields.io/badge/build-passing-brightgreen.svg)](https://github.com/lduchosal/check_msdefender)
6
6
 
@@ -1,4 +1,4 @@
1
1
  """Check Microsoft Defender API endpoints and check values - Nagios plugin."""
2
- __version__ = "1.1.13"
2
+ __version__ = "1.1.15"
3
3
  __author__ = "ldvchosal"
4
4
  __email__ = "ldvchosa@github.com"
@@ -129,12 +129,9 @@ class NagiosPlugin:
129
129
  DefenderSummary(details),
130
130
  )
131
131
 
132
- # Set verbosity
133
- check.verbosity = verbose
134
-
135
132
  # Run check and return exit code instead of exiting
136
133
  try:
137
- check.main()
134
+ check.main(verbose=verbose)
138
135
  return 0 # If main() doesn't exit, it's OK
139
136
  except SystemExit as e:
140
137
  return int(e.code) if e.code is not None else 0
@@ -6,6 +6,7 @@ from datetime import datetime
6
6
  from check_msdefender.core.exceptions import ValidationError
7
7
  from check_msdefender.core.logging_config import get_verbose_logger
8
8
 
9
+
9
10
  class DetailObject:
10
11
  def __init__(self, software: str, data: str, score: int):
11
12
  self.software = software
@@ -13,6 +14,7 @@ class DetailObject:
13
14
  self.score = score
14
15
  self.paths: list[str] = []
15
16
 
17
+
16
18
  class ProductsService:
17
19
  """Service for checking installed products on machines."""
18
20
 
@@ -55,7 +57,7 @@ class ProductsService:
55
57
  product for product in all_products if product.get("deviceId") == target_machine_id
56
58
  ]
57
59
 
58
- self.logger.info(f"Found {len(products)} CVE vulnerabilities for machine {target_dns_name}")
60
+ self.logger.info(f"Found {len(products)} vulnerabilities for machine {target_dns_name}")
59
61
 
60
62
  # Group vulnerabilities by software
61
63
  software_vulnerabilities = {}
@@ -80,7 +82,7 @@ class ProductsService:
80
82
  "paths": set(),
81
83
  "registryPaths": set(),
82
84
  "max_cvss": 0,
83
- "severities": set(),
85
+ "severities": [],
84
86
  }
85
87
 
86
88
  cve_info = {"cve_id": cve_id, "severity": severity}
@@ -90,7 +92,7 @@ class ProductsService:
90
92
  software_vulnerabilities[software_key]["max_cvss"] = max(
91
93
  software_vulnerabilities[software_key]["max_cvss"], cvss_score
92
94
  )
93
- software_vulnerabilities[software_key]["severities"].add(severity)
95
+ software_vulnerabilities[software_key]["severities"].append(severity)
94
96
 
95
97
  # Count vulnerabilities by severity
96
98
  critical_count = 0
@@ -119,11 +121,9 @@ class ProductsService:
119
121
  details = []
120
122
  total_score = 0
121
123
  if software_vulnerabilities:
122
- summary_line = f"{len(products)} total CVEs (Critical: {critical_count}, High: {high_count}, Medium: {medium_count}, Low: {low_count}), {len(vulnerable_software)} vulnerable software"
123
- details.append(summary_line)
124
124
 
125
125
  detail_objects = []
126
-
126
+
127
127
  # Add software details
128
128
  for software in list(software_vulnerabilities.values()):
129
129
  score = 0
@@ -131,7 +131,15 @@ class ProductsService:
131
131
  cve_count = len(software["cves"])
132
132
  unique_cves = list(set(cve["cve_id"] for cve in software["cves"]))
133
133
  cve_list = ", ".join(unique_cves[:5]) # Show first 5 CVEs
134
- severities = ", ".join(software["severities"]) # Show first 5 CVEs
134
+ severities = ""
135
+ # Count severities
136
+ severity_counts = {"Critical": 0, "High": 0, "Medium": 0, "Low": 0}
137
+ for sev in software["severities"]:
138
+ severity_counts[sev] += 1
139
+ severities = ", ".join(
140
+ f"{key}: {value}" for key, value in severity_counts.items() if value > 0
141
+ )
142
+
135
143
  for cve in software["cves"]:
136
144
  severity = cve["severity"].lower()
137
145
  if severity == "critical":
@@ -148,8 +156,8 @@ class ProductsService:
148
156
 
149
157
  detail_object = DetailObject(
150
158
  software=f"{software['name']} {software['version']} ({software['vendor']})",
151
- data=f"{score} ({cve_count}: {severities}) weaknesses ({cve_list})",
152
- score=score
159
+ data=f"Score: {score}, CVEs: {cve_count} ({severities}), ({cve_list})",
160
+ score=score,
153
161
  )
154
162
 
155
163
  total_score += score
@@ -159,7 +167,7 @@ class ProductsService:
159
167
  detail_object.paths.append(f" - {path}")
160
168
 
161
169
  # Indicate if more paths exist
162
- if (len(software["paths"]) > 4):
170
+ if len(software["paths"]) > 4:
163
171
  detail_object.paths.append(f" - .. (+{len(software['paths']) - 4} more)")
164
172
 
165
173
  # Add registry paths if available (limit to 4)
@@ -167,19 +175,26 @@ class ProductsService:
167
175
  detail_object.paths.append(f" - {registry_path}")
168
176
 
169
177
  # Indicate if more registry paths exist
170
- if (len(software["registryPaths"]) > 4):
171
- detail_object.paths.append(f" - .. (+{len(software['registryPaths']) - 4} more)")
178
+ if len(software["registryPaths"]) > 4:
179
+ detail_object.paths.append(
180
+ f" - .. (+{len(software['registryPaths']) - 4} more)"
181
+ )
172
182
 
173
183
  # Collect detail objects for sorting
174
184
  detail_objects.append(detail_object)
175
185
 
186
+ summary_line = f"{len(vulnerable_software)} vulnerable products, score: {total_score}"
187
+ details.append(summary_line)
188
+ details.append("")
189
+
176
190
  # Sort detail objects by score descending
177
191
  detail_objects.sort(key=lambda x: x.score, reverse=True)
178
-
192
+
179
193
  # Limit to top 10
180
194
  for detail_object in detail_objects[:10]:
181
- details.append(f"{detail_object.software} {detail_object.data}")
195
+ details.append(f"{detail_object.software} - {detail_object.data}")
182
196
  details.extend(detail_object.paths)
197
+ details.append("")
183
198
 
184
199
  # Determine the value based on severity:
185
200
  # - Critical vulnerabilities trigger critical threshold
@@ -198,9 +213,7 @@ class ProductsService:
198
213
  }
199
214
 
200
215
  self.logger.info(
201
- f"Products analysis complete: {len(products)} total CVEs "
202
- f"(Critical: {critical_count}, High: {high_count}, Medium: {medium_count}, Low: {low_count}), "
203
- f"{len(vulnerable_software)} vulnerable software"
216
+ f"Products analysis complete: {len(vulnerable_software)} vulnerable products, score: {total_score}"
204
217
  )
205
218
  self.logger.method_exit("get_result", result)
206
219
  return result
@@ -12,16 +12,17 @@ authors = [
12
12
  ]
13
13
  description = "A Nagios plugin for monitoring Microsoft Defender API endpoints"
14
14
  readme = "README.md"
15
- requires-python = ">=3.9"
15
+ requires-python = ">=3.10"
16
16
  classifiers = [
17
17
  "Development Status :: 5 - Production/Stable",
18
18
  "Intended Audience :: System Administrators",
19
19
  "License :: OSI Approved :: MIT License",
20
20
  "Operating System :: OS Independent",
21
21
  "Programming Language :: Python :: 3",
22
- "Programming Language :: Python :: 3.9",
23
22
  "Programming Language :: Python :: 3.10",
24
23
  "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Programming Language :: Python :: 3.13",
25
26
  "Topic :: System :: Monitoring",
26
27
  "Topic :: System :: Systems Administration",
27
28
  ]
@@ -38,7 +39,7 @@ dependencies = [
38
39
  "azure-identity>=1.12.0",
39
40
  "click>=8.0,<9.0",
40
41
  ]
41
- version = "1.1.13"
42
+ version = "1.1.15"
42
43
 
43
44
  [project.license]
44
45
  text = "MIT"
@@ -128,7 +129,7 @@ composite = [
128
129
  [tool.black]
129
130
  line-length = 100
130
131
  target-version = [
131
- "py39",
132
+ "py310",
132
133
  ]
133
134
 
134
135
  [tool.mypy]
@@ -81,6 +81,7 @@ class TestDetailServiceFixtures:
81
81
  """Test that all expected machine detail fields are present."""
82
82
  self.service.get_result(machine_id="test-machine-3")
83
83
  details = self.service.get_machine_details_json()
84
+ assert details is not None
84
85
  details_dict = json.loads(details)
85
86
 
86
87
  # Verify all expected fields from the documentation are present
@@ -122,6 +123,7 @@ class TestDetailServiceFixtures:
122
123
  details = self.service.get_machine_details_json()
123
124
 
124
125
  # Should be valid JSON
126
+ assert details is not None
125
127
  details_dict = json.loads(details)
126
128
  assert isinstance(details_dict, dict)
127
129