check-msdefender 1.1.11__tar.gz → 1.1.14__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.11 → check_msdefender-1.1.14}/PKG-INFO +1 -1
  2. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/check_msdefender/__init__.py +1 -1
  3. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/check_msdefender/core/nagios.py +1 -4
  4. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/check_msdefender/services/products_service.py +72 -20
  5. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/pyproject.toml +1 -1
  6. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/tests/fixtures/test_detail_service.py +2 -0
  7. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/LICENSE +0 -0
  8. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/README.md +0 -0
  9. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/check_msdefender/__main__.py +0 -0
  10. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/check_msdefender/check_msdefender.py +0 -0
  11. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/check_msdefender/cli/__init__.py +0 -0
  12. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/check_msdefender/cli/__main__.py +0 -0
  13. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/check_msdefender/cli/commands/__init__.py +0 -0
  14. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/check_msdefender/cli/commands/alerts.py +0 -0
  15. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/check_msdefender/cli/commands/detail.py +0 -0
  16. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/check_msdefender/cli/commands/lastseen.py +0 -0
  17. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/check_msdefender/cli/commands/machines.py +0 -0
  18. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/check_msdefender/cli/commands/onboarding.py +0 -0
  19. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/check_msdefender/cli/commands/products.py +0 -0
  20. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/check_msdefender/cli/commands/vulnerabilities.py +0 -0
  21. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/check_msdefender/cli/decorators.py +0 -0
  22. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/check_msdefender/cli/handlers.py +0 -0
  23. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/check_msdefender/core/__init__.py +0 -0
  24. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/check_msdefender/core/auth.py +0 -0
  25. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/check_msdefender/core/config.py +0 -0
  26. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/check_msdefender/core/defender.py +0 -0
  27. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/check_msdefender/core/exceptions.py +0 -0
  28. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/check_msdefender/core/logging_config.py +0 -0
  29. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/check_msdefender/services/__init__.py +0 -0
  30. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/check_msdefender/services/alerts_service.py +0 -0
  31. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/check_msdefender/services/detail_service.py +0 -0
  32. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/check_msdefender/services/lastseen_service.py +0 -0
  33. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/check_msdefender/services/machines_service.py +0 -0
  34. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/check_msdefender/services/models.py +0 -0
  35. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/check_msdefender/services/onboarding_service.py +0 -0
  36. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/check_msdefender/services/vulnerabilities_service.py +0 -0
  37. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/tests/__init__.py +0 -0
  38. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/tests/fixtures/__init__.py +0 -0
  39. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/tests/fixtures/alerts_data.json +0 -0
  40. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/tests/fixtures/machine_data.json +0 -0
  41. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/tests/fixtures/mock_defender_client.py +0 -0
  42. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/tests/fixtures/test_alerts_service.py +0 -0
  43. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/tests/fixtures/test_lastseen_service.py +0 -0
  44. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/tests/fixtures/test_onboarding_service.py +0 -0
  45. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/tests/fixtures/test_vulnerabilities_service.py +0 -0
  46. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/tests/fixtures/vulnerability_data.json +0 -0
  47. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/tests/integration/__init__.py +0 -0
  48. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/tests/integration/test_cli_integration.py +0 -0
  49. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/tests/integration/test_lastseen_integration.py +0 -0
  50. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/tests/unit/__init__.py +0 -0
  51. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/tests/unit/test_alerts_service.py +0 -0
  52. {check_msdefender-1.1.11 → check_msdefender-1.1.14}/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.11
3
+ Version: 1.1.14
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>
@@ -1,4 +1,4 @@
1
1
  """Check Microsoft Defender API endpoints and check values - Nagios plugin."""
2
- __version__ = "1.1.11"
2
+ __version__ = "1.1.14"
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
@@ -7,6 +7,14 @@ 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:
11
+ def __init__(self, software: str, data: str, score: int):
12
+ self.software = software
13
+ self.data = data
14
+ self.score = score
15
+ self.paths: list[str] = []
16
+
17
+
10
18
  class ProductsService:
11
19
  """Service for checking installed products on machines."""
12
20
 
@@ -49,7 +57,7 @@ class ProductsService:
49
57
  product for product in all_products if product.get("deviceId") == target_machine_id
50
58
  ]
51
59
 
52
- 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}")
53
61
 
54
62
  # Group vulnerabilities by software
55
63
  software_vulnerabilities = {}
@@ -60,6 +68,7 @@ class ProductsService:
60
68
  cve_id = vulnerability.get("cveId", "Unknown")
61
69
  cvss_score = vulnerability.get("cvssScore", 0)
62
70
  disk_paths = vulnerability.get("diskPaths", [])
71
+ registry_paths = vulnerability.get("registryPaths", [])
63
72
  severity = vulnerability.get("vulnerabilitySeverityLevel", "Unknown")
64
73
 
65
74
  software_key = f"{software_name}-{software_version}-{software_vendor}"
@@ -71,16 +80,19 @@ class ProductsService:
71
80
  "vendor": software_vendor,
72
81
  "cves": [],
73
82
  "paths": set(),
83
+ "registryPaths": set(),
74
84
  "max_cvss": 0,
75
- "severities": set(),
85
+ "severities": [],
76
86
  }
77
87
 
78
- software_vulnerabilities[software_key]["cves"].append(cve_id)
88
+ cve_info = {"cve_id": cve_id, "severity": severity}
89
+ software_vulnerabilities[software_key]["cves"].append(cve_info)
79
90
  software_vulnerabilities[software_key]["paths"].update(disk_paths)
91
+ software_vulnerabilities[software_key]["registryPaths"].update(registry_paths)
80
92
  software_vulnerabilities[software_key]["max_cvss"] = max(
81
93
  software_vulnerabilities[software_key]["max_cvss"], cvss_score
82
94
  )
83
- software_vulnerabilities[software_key]["severities"].add(severity)
95
+ software_vulnerabilities[software_key]["severities"].append(severity)
84
96
 
85
97
  # Count vulnerabilities by severity
86
98
  critical_count = 0
@@ -109,18 +121,27 @@ class ProductsService:
109
121
  details = []
110
122
  total_score = 0
111
123
  if software_vulnerabilities:
112
- summary_line = f"{len(products)} total CVEs (Critical: {critical_count}, High: {high_count}, Medium: {medium_count}, Low: {low_count}), {len(vulnerable_software)} vulnerable software"
113
- details.append(summary_line)
114
124
 
115
- score = 0
116
- # Add software details (limit to 10)
117
- for software in list(software_vulnerabilities.values())[:10]:
125
+ detail_objects = []
126
+
127
+ # Add software details
128
+ for software in list(software_vulnerabilities.values()):
129
+ score = 0
130
+
118
131
  cve_count = len(software["cves"])
119
- unique_cves = list(set(software["cves"]))
132
+ unique_cves = list(set(cve["cve_id"] for cve in software["cves"]))
120
133
  cve_list = ", ".join(unique_cves[:5]) # Show first 5 CVEs
121
- severities = ", ".join(software["severities"]) # Show first 5 CVEs
122
- for severity_name in software["severities"]:
123
- severity = severity_name.lower()
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
+
143
+ for cve in software["cves"]:
144
+ severity = cve["severity"].lower()
124
145
  if severity == "critical":
125
146
  score += 100
126
147
  elif severity == "high":
@@ -133,14 +154,47 @@ class ProductsService:
133
154
  if len(unique_cves) > 5:
134
155
  cve_list += f".. (+{len(unique_cves) - 5} more)"
135
156
 
136
- details.append(
137
- f"{software['name']} {software['version']} ({software['vendor']}) - "
138
- f"{score} ({cve_count}: {severities}) weaknesses ({cve_list})"
157
+ detail_object = DetailObject(
158
+ software=f"{software['name']} {software['version']} ({software['vendor']})",
159
+ data=f"Score: {score}, CVEs: {cve_count} ({severities}), ({cve_list})",
160
+ score=score,
139
161
  )
162
+
140
163
  total_score += score
164
+
141
165
  # Add paths (limit to 4)
142
166
  for path in list(software["paths"])[:4]:
143
- details.append(f" - {path}")
167
+ detail_object.paths.append(f" - {path}")
168
+
169
+ # Indicate if more paths exist
170
+ if len(software["paths"]) > 4:
171
+ detail_object.paths.append(f" - .. (+{len(software['paths']) - 4} more)")
172
+
173
+ # Add registry paths if available (limit to 4)
174
+ for registry_path in list(software["registryPaths"])[:4]:
175
+ detail_object.paths.append(f" - {registry_path}")
176
+
177
+ # Indicate if more registry paths exist
178
+ if len(software["registryPaths"]) > 4:
179
+ detail_object.paths.append(
180
+ f" - .. (+{len(software['registryPaths']) - 4} more)"
181
+ )
182
+
183
+ # Collect detail objects for sorting
184
+ detail_objects.append(detail_object)
185
+
186
+ summary_line = f"{len(vulnerable_software)} vulnerable products, score: {total_score}"
187
+ details.append(summary_line)
188
+ details.append("")
189
+
190
+ # Sort detail objects by score descending
191
+ detail_objects.sort(key=lambda x: x.score, reverse=True)
192
+
193
+ # Limit to top 10
194
+ for detail_object in detail_objects[:10]:
195
+ details.append(f"{detail_object.software} - {detail_object.data}")
196
+ details.extend(detail_object.paths)
197
+ details.append("")
144
198
 
145
199
  # Determine the value based on severity:
146
200
  # - Critical vulnerabilities trigger critical threshold
@@ -159,9 +213,7 @@ class ProductsService:
159
213
  }
160
214
 
161
215
  self.logger.info(
162
- f"Products analysis complete: {len(products)} total CVEs "
163
- f"(Critical: {critical_count}, High: {high_count}, Medium: {medium_count}, Low: {low_count}), "
164
- f"{len(vulnerable_software)} vulnerable software"
216
+ f"Products analysis complete: {len(vulnerable_software)} vulnerable products, score: {total_score}"
165
217
  )
166
218
  self.logger.method_exit("get_result", result)
167
219
  return result
@@ -38,7 +38,7 @@ dependencies = [
38
38
  "azure-identity>=1.12.0",
39
39
  "click>=8.0,<9.0",
40
40
  ]
41
- version = "1.1.11"
41
+ version = "1.1.14"
42
42
 
43
43
  [project.license]
44
44
  text = "MIT"
@@ -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