check-msdefender 1.1.6__py3-none-any.whl → 1.1.7__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,5 +1,4 @@
1
1
  """Check Microsoft Defender API endpoints and check values - Nagios plugin."""
2
-
3
- __version__ = "1.0.0"
2
+ __version__ = "1.1.7"
4
3
  __author__ = "ldvchosal"
5
4
  __email__ = "ldvchosa@github.com"
@@ -7,6 +7,7 @@ from .onboarding import register_onboarding_commands
7
7
  from .machines import register_machines_commands
8
8
  from .detail import register_detail_commands
9
9
  from .alerts import register_alerts_commands
10
+ from .products import register_products_commands
10
11
 
11
12
 
12
13
  def register_all_commands(main_group: Any) -> None:
@@ -17,3 +18,4 @@ def register_all_commands(main_group: Any) -> None:
17
18
  register_machines_commands(main_group)
18
19
  register_detail_commands(main_group)
19
20
  register_alerts_commands(main_group)
21
+ register_products_commands(main_group)
@@ -0,0 +1,60 @@
1
+ """Products commands for CLI."""
2
+
3
+ import sys
4
+ from typing import Optional, Any
5
+
6
+ from check_msdefender.core.auth import get_authenticator
7
+ from check_msdefender.core.config import load_config
8
+ from check_msdefender.core.defender import DefenderClient
9
+ from check_msdefender.core.nagios import NagiosPlugin
10
+ from check_msdefender.services.products_service import ProductsService
11
+ from ..decorators import common_options
12
+
13
+
14
+ def register_products_commands(main_group: Any) -> None:
15
+ """Register products commands with the main CLI group."""
16
+
17
+ @main_group.command("products")
18
+ @common_options
19
+ def products_cmd(
20
+ config: str,
21
+ verbose: int,
22
+ machine_id: Optional[str],
23
+ dns_name: Optional[str],
24
+ warning: Optional[float],
25
+ critical: Optional[float],
26
+ ) -> None:
27
+ """Check installed products for Microsoft Defender."""
28
+ warning = warning if warning is not None else 5
29
+ critical = critical if critical is not None else 1
30
+
31
+ try:
32
+ # Load configuration
33
+ cfg = load_config(config)
34
+
35
+ # Get authenticator
36
+ authenticator = get_authenticator(cfg)
37
+
38
+ # Create Defender client
39
+ client = DefenderClient(authenticator, verbose_level=verbose)
40
+
41
+ # Create the products service
42
+ service = ProductsService(client, verbose_level=verbose)
43
+
44
+ # Create Nagios plugin
45
+ plugin = NagiosPlugin(service, "products")
46
+
47
+ # Execute check
48
+ result = plugin.check(
49
+ machine_id=machine_id,
50
+ dns_name=dns_name,
51
+ warning=warning,
52
+ critical=critical,
53
+ verbose=verbose,
54
+ )
55
+
56
+ sys.exit(result or 0)
57
+
58
+ except Exception as e:
59
+ print(f"UNKNOWN: {str(e)}")
60
+ sys.exit(3)
@@ -10,9 +10,7 @@ def common_options(func: Callable[..., Any]) -> Callable[..., Any]:
10
10
  "-c", "--config", default="check_msdefender.ini", help="Configuration file path"
11
11
  )(func)
12
12
  func = click.option("-v", "--verbose", count=True, help="Increase verbosity")(func)
13
- func = click.option("-m", "--machine-id", "-i", "--id", help="Machine ID (GUID)")(
14
- func
15
- )
13
+ func = click.option("-m", "--machine-id", "-i", "--id", help="Machine ID (GUID)")(func)
16
14
  func = click.option("-d", "--dns-name", help="Computer DNS Name (FQDN)")(func)
17
15
  func = click.option("-W", "--warning", type=float, help="Warning threshold")(func)
18
16
  func = click.option("-C", "--critical", type=float, help="Critical threshold")(func)
@@ -20,9 +20,7 @@ def get_authenticator(
20
20
  tenant_id = auth_section.get("tenant_id")
21
21
 
22
22
  if not client_id or not tenant_id:
23
- raise ConfigurationError(
24
- "client_id and tenant_id are required in [auth] section"
25
- )
23
+ raise ConfigurationError("client_id and tenant_id are required in [auth] section")
26
24
 
27
25
  # Check for client secret authentication
28
26
  client_secret = auth_section.get("client_secret")
@@ -6,6 +6,16 @@ from typing import Any, Dict, cast
6
6
  from check_msdefender.core.exceptions import DefenderAPIError
7
7
  from check_msdefender.core.logging_config import get_verbose_logger
8
8
 
9
+ PARAM_TOP = "$top"
10
+
11
+ PARAM_EXPAND = "$expand"
12
+
13
+ PARAM_ORDERBY = "$orderby"
14
+
15
+ PARAM_FILTER = "$filter"
16
+
17
+ PARAM_SELECT = "$select"
18
+
9
19
 
10
20
  class DefenderClient:
11
21
  """Client for Microsoft Defender API."""
@@ -55,14 +65,15 @@ class DefenderClient:
55
65
  "Content-Type": DefenderClient.application_json,
56
66
  }
57
67
 
58
- params = {"$filter": f"computerDnsName eq '{dns_name}'", "$select": "id"}
68
+ params = {
69
+ PARAM_FILTER: f"computerDnsName eq '{dns_name}'",
70
+ PARAM_SELECT: "id"
71
+ }
59
72
 
60
73
  try:
61
74
  start_time = time.time()
62
75
  self.logger.info(f"Querying machine by DNS name: {dns_name}")
63
- response = requests.get(
64
- url, headers=headers, params=params, timeout=self.timeout
65
- )
76
+ response = requests.get(url, headers=headers, params=params, timeout=self.timeout)
66
77
  elapsed = time.time() - start_time
67
78
 
68
79
  self.logger.api_call("GET", url, response.status_code, elapsed)
@@ -152,14 +163,12 @@ class DefenderClient:
152
163
  "Content-Type": DefenderClient.application_json,
153
164
  }
154
165
 
155
- params = {"$select": "id,computerDnsName,onboardingStatus,osPlatform"}
166
+ params = {PARAM_SELECT: "id,computerDnsName,onboardingStatus,osPlatform"}
156
167
 
157
168
  try:
158
169
  start_time = time.time()
159
170
  self.logger.info("Querying all machines")
160
- response = requests.get(
161
- url, headers=headers, params=params, timeout=self.timeout
162
- )
171
+ response = requests.get(url, headers=headers, params=params, timeout=self.timeout)
163
172
  elapsed = time.time() - start_time
164
173
 
165
174
  self.logger.api_call("GET", url, response.status_code, elapsed)
@@ -188,18 +197,16 @@ class DefenderClient:
188
197
  }
189
198
 
190
199
  params = {
191
- "$top": "100",
192
- "$expand": "evidence",
193
- "$orderby": "alertCreationTime desc",
194
- "$select": "status,title,machineId,computerDnsName,alertCreationTime,firstEventTime,lastEventTime,lastUpdateTime,severity",
200
+ PARAM_TOP: "100",
201
+ PARAM_EXPAND: "evidence",
202
+ PARAM_ORDERBY: "alertCreationTime desc",
203
+ PARAM_SELECT: "status,title,machineId,computerDnsName,alertCreationTime,firstEventTime,lastEventTime,lastUpdateTime,severity",
195
204
  }
196
205
 
197
206
  try:
198
207
  start_time = time.time()
199
208
  self.logger.info("Querying alerts")
200
- response = requests.get(
201
- url, headers=headers, params=params, timeout=self.timeout
202
- )
209
+ response = requests.get(url, headers=headers, params=params, timeout=self.timeout)
203
210
  elapsed = time.time() - start_time
204
211
 
205
212
  self.logger.api_call("GET", url, response.status_code, elapsed)
@@ -215,6 +222,40 @@ class DefenderClient:
215
222
  self.logger.debug(f"Response: {str(e.response.content)}")
216
223
  raise DefenderAPIError(f"Failed to query MS Defender API: {str(e)}")
217
224
 
225
+ def get_products(self) -> Dict[str, Any]:
226
+ """Get installed products for a machine."""
227
+ self.logger.method_entry("get_products")
228
+
229
+ token = self._get_token()
230
+
231
+ # Use the TVM API endpoint for products
232
+ url = f"{self.base_url}/api/machines/SoftwareVulnerabilitiesByMachine"
233
+ headers = {
234
+ "Authorization": f"Bearer {token}",
235
+ "Content-Type": DefenderClient.application_json,
236
+ }
237
+
238
+ params = {"pageIndex": "1", "pageSize": "50000"}
239
+
240
+ try:
241
+ start_time = time.time()
242
+ self.logger.info("Querying products")
243
+ response = requests.get(url, headers=headers, params=params, timeout=self.timeout)
244
+ elapsed = time.time() - start_time
245
+
246
+ self.logger.api_call("GET", url, response.status_code, elapsed)
247
+ response.raise_for_status()
248
+
249
+ result = cast(Dict[str, Any], response.json())
250
+ self.logger.json_response(str(result))
251
+ self.logger.method_exit("get_products", result)
252
+ return result
253
+ except requests.RequestException as e:
254
+ self.logger.debug(f"API request failed: {str(e)}")
255
+ if hasattr(e, "response") and e.response is not None:
256
+ self.logger.debug(f"Response: {str(e.response.content)}")
257
+ raise DefenderAPIError(f"Failed to query MS Defender API: {str(e)}")
258
+
218
259
  def _get_token(self) -> str:
219
260
  """Get access token from authenticator."""
220
261
  self.logger.trace("Getting access token from authenticator")
@@ -82,9 +82,7 @@ class VerboseLogger:
82
82
  """Log API call details if verbose >= 2."""
83
83
  if self.verbose_level >= 2:
84
84
  if status_code and response_time:
85
- self.logger.debug(
86
- f"API {method} {url} -> {status_code} ({response_time:.3f}s)"
87
- )
85
+ self.logger.debug(f"API {method} {url} -> {status_code} ({response_time:.3f}s)")
88
86
  else:
89
87
  self.logger.debug(f"API {method} {url}")
90
88
 
@@ -50,12 +50,6 @@ class DefenderScalarContext(nagiosplugin.ScalarContext):
50
50
  f"{metric.name} is {metric.value} (outside range {warning_val}:)",
51
51
  metric,
52
52
  )
53
- elif critical_val == metric.value:
54
- return self.result_cls(
55
- nagiosplugin.Critical,
56
- f"{metric.name} is {metric.value} (outside range {critical_val}:)",
57
- metric,
58
- )
59
53
  else:
60
54
  # If no exact match, use the more severe one (critical)
61
55
  return self.result_cls(
@@ -128,9 +122,7 @@ class NagiosPlugin:
128
122
 
129
123
  # Create Nagios check with custom summary
130
124
  # Use 'found' as context name for detail command, otherwise use command name
131
- context_name = (
132
- "found" if self.command_name == "detail" else self.command_name
133
- )
125
+ context_name = "found" if self.command_name == "detail" else self.command_name
134
126
  check = nagiosplugin.Check(
135
127
  DefenderResource(self.command_name, value),
136
128
  DefenderScalarContext(context_name, warning, critical),
@@ -53,18 +53,12 @@ class AlertsService:
53
53
  or alert.get("computerDnsName") == target_dns_name
54
54
  ]
55
55
 
56
- self.logger.info(
57
- f"Found {len(machine_alerts)} alerts for machine {target_dns_name}"
58
- )
56
+ self.logger.info(f"Found {len(machine_alerts)} alerts for machine {target_dns_name}")
59
57
 
60
58
  # Categorize alerts by status and severity
61
- unresolved_alerts = [
62
- alert for alert in machine_alerts if alert.get("status") != "Resolved"
63
- ]
59
+ unresolved_alerts = [alert for alert in machine_alerts if alert.get("status") != "Resolved"]
64
60
  informational_alerts = [
65
- alert
66
- for alert in unresolved_alerts
67
- if alert.get("severity") == "Informational"
61
+ alert for alert in unresolved_alerts if alert.get("severity") == "Informational"
68
62
  ]
69
63
  critical_warning_alerts = [
70
64
  alert
@@ -88,9 +82,7 @@ class AlertsService:
88
82
  title = alert.get("title", "Unknown alert")
89
83
  status = alert.get("status", "Unknown")
90
84
  severity = alert.get("severity", "Unknown")
91
- details.append(
92
- f"{creation_time} - {title} ({status} {severity.lower()})"
93
- )
85
+ details.append(f"{creation_time} - {title} ({status} {severity.lower()})")
94
86
 
95
87
  # Return the number of unresolved alerts as the value
96
88
  # This will be used by Nagios plugin for determining status based on thresholds
@@ -101,8 +93,6 @@ class AlertsService:
101
93
  "details": details,
102
94
  }
103
95
 
104
- self.logger.info(
105
- f"Alert analysis complete: {len(unresolved_alerts)} unresolved alerts"
106
- )
96
+ self.logger.info(f"Alert analysis complete: {len(unresolved_alerts)} unresolved alerts")
107
97
  self.logger.method_exit("get_result", result)
108
98
  return result
@@ -54,21 +54,15 @@ class DetailService:
54
54
  # Create detailed output
55
55
  details = []
56
56
  details.append(f"Machine ID: {machine_details.get('id', 'Unknown')}")
57
- details.append(
58
- f"Computer Name: {machine_details.get('computerDnsName', 'Unknown')}"
59
- )
60
- details.append(
61
- f"OS Platform: {machine_details.get('osPlatform', 'Unknown')}"
62
- )
57
+ details.append(f"Computer Name: {machine_details.get('computerDnsName', 'Unknown')}")
58
+ details.append(f"OS Platform: {machine_details.get('osPlatform', 'Unknown')}")
63
59
  details.append(f"OS Version: {machine_details.get('osVersion', 'Unknown')}")
64
- details.append(
65
- f"Health Status: {machine_details.get('healthStatus', 'Unknown')}"
66
- )
60
+ details.append(f"Health Status: {machine_details.get('healthStatus', 'Unknown')}")
67
61
  details.append(f"Risk Score: {machine_details.get('riskScore', 'Unknown')}")
68
62
 
69
63
  result = {"value": 1, "details": details}
70
64
 
71
- self.logger.info(f"Machine details retrieved successfully")
65
+ self.logger.info("Machine details retrieved successfully")
72
66
  self.logger.method_exit("get_result", result)
73
67
  return result
74
68
 
@@ -62,9 +62,7 @@ class LastSeenService:
62
62
 
63
63
  result = {"value": days_diff, "details": details}
64
64
 
65
- self.logger.info(
66
- f"Machine last seen {days_diff} days ago ({last_seen_str})"
67
- )
65
+ self.logger.info(f"Machine last seen {days_diff} days ago ({last_seen_str})")
68
66
  self.logger.method_exit("get_result", result)
69
67
  return result
70
68
  except (ValueError, TypeError) as e:
@@ -13,7 +13,7 @@ class MachinesService:
13
13
  self.defender = defender_client
14
14
  self.logger = get_verbose_logger(__name__, verbose_level)
15
15
 
16
- def get_result(self, **kwargs: Any) -> Dict[str, Any]:
16
+ def get_result(self) -> Dict[str, Any]:
17
17
  """Get machine count result with value and details."""
18
18
  self.logger.method_entry("get_result")
19
19
 
@@ -44,8 +44,8 @@ class MachinesService:
44
44
  sorted_machines = sorted(
45
45
  machines,
46
46
  key=lambda x: (
47
- status_priority[x["onboardingStatus"]],
48
- x["computerDnsName"],
47
+ status_priority[x["onboardingStatus"] or ""],
48
+ x["computerDnsName"] or "",
49
49
  ),
50
50
  )
51
51
  for machine in sorted_machines:
@@ -60,7 +60,7 @@ class MachinesService:
60
60
  self.logger.method_exit("get_result", result)
61
61
  return result
62
62
 
63
- def get_details(self, **kwargs: Any) -> List[str]:
63
+ def get_details(self) -> List[str]:
64
64
  """Get detailed machine information."""
65
65
  self.logger.method_entry("get_details")
66
66
 
@@ -54,8 +54,6 @@ class OnboardingService:
54
54
 
55
55
  result = {"value": result_value, "details": details}
56
56
 
57
- self.logger.info(
58
- f"Machine onboarding status: {onboarding_state} -> {result_value}"
59
- )
57
+ self.logger.info(f"Machine onboarding status: {onboarding_state} -> {result_value}")
60
58
  self.logger.method_exit("get_result", result)
61
59
  return result
@@ -0,0 +1,136 @@
1
+ """Products service implementation."""
2
+
3
+ from typing import Dict, Optional, Any
4
+ from datetime import datetime
5
+
6
+ from check_msdefender.core.exceptions import ValidationError
7
+ from check_msdefender.core.logging_config import get_verbose_logger
8
+
9
+
10
+ class ProductsService:
11
+ """Service for checking installed products on machines."""
12
+
13
+ def __init__(self, defender_client: Any, verbose_level: int = 0) -> None:
14
+ """Initialize with Defender client."""
15
+ self.defender = defender_client
16
+ self.logger = get_verbose_logger(__name__, verbose_level)
17
+
18
+ def get_result(
19
+ self, machine_id: Optional[str] = None, dns_name: Optional[str] = None
20
+ ) -> Dict[str, Any]:
21
+ """Get products result with value and details for a machine."""
22
+ self.logger.method_entry("get_result", machine_id=machine_id, dns_name=dns_name)
23
+
24
+ if not machine_id and not dns_name:
25
+ raise ValidationError("Either machine_id or dns_name must be provided")
26
+
27
+ # Get machine information
28
+ target_dns_name = dns_name
29
+ target_machine_id = machine_id
30
+
31
+ if machine_id:
32
+ # Get DNS name from machine_id
33
+ machine_details = self.defender.get_machine_by_id(machine_id)
34
+ target_dns_name = machine_details.get("computerDnsName", "Unknown")
35
+ elif dns_name:
36
+ # Get machine_id from dns_name
37
+ dns_response = self.defender.get_machine_by_dns_name(dns_name)
38
+ machines = dns_response.get("value", [])
39
+ if not machines:
40
+ raise ValidationError(f"Machine not found with DNS name: {dns_name}")
41
+ target_machine_id = machines[0].get("id")
42
+ target_dns_name = dns_name
43
+
44
+ # Get products for the machine
45
+ self.logger.info("Fetching products from Microsoft Defender")
46
+ products_data = self.defender.get_products()
47
+ all_products = products_data.get("value", [])
48
+ products = [
49
+ product for product in all_products if product.get("deviceId") == target_machine_id
50
+ ]
51
+
52
+ self.logger.info(f"Found {len(products)} CVE vulnerabilities for machine {target_dns_name}")
53
+
54
+ # Group vulnerabilities by software
55
+ software_vulnerabilities = {}
56
+ for vulnerability in products:
57
+ software_name = vulnerability.get("softwareName", "Unknown")
58
+ software_version = vulnerability.get("softwareVersion", "Unknown")
59
+ software_vendor = vulnerability.get("softwareVendor", "Unknown")
60
+ cve_id = vulnerability.get("cveId", "Unknown")
61
+ cvss_score = vulnerability.get("cvssScore", 0)
62
+ disk_paths = vulnerability.get("diskPaths", [])
63
+ severity = vulnerability.get("vulnerabilitySeverityLevel", "Unknown")
64
+
65
+ software_key = f"{software_name}-{software_version}-{software_vendor}"
66
+
67
+ if software_key not in software_vulnerabilities:
68
+ software_vulnerabilities[software_key] = {
69
+ "name": software_name,
70
+ "version": software_version,
71
+ "vendor": software_vendor,
72
+ "cves": [],
73
+ "paths": set(),
74
+ "max_cvss": 0,
75
+ "severities": set(),
76
+ }
77
+
78
+ software_vulnerabilities[software_key]["cves"].append(cve_id)
79
+ software_vulnerabilities[software_key]["paths"].update(disk_paths)
80
+ software_vulnerabilities[software_key]["max_cvss"] = max(
81
+ software_vulnerabilities[software_key]["max_cvss"], cvss_score
82
+ )
83
+ software_vulnerabilities[software_key]["severities"].add(severity)
84
+
85
+ # Count vulnerable software
86
+ vulnerable_software = []
87
+
88
+ for software in software_vulnerabilities.values():
89
+ if len(software["cves"]) > 0:
90
+ vulnerable_software.append(software)
91
+
92
+ # Create details for output
93
+ details = []
94
+ if software_vulnerabilities:
95
+ summary_line = f"{len(products)} CVE found on {target_dns_name}"
96
+ details.append(summary_line)
97
+
98
+ # Add software details (limit to 10)
99
+ for software in list(software_vulnerabilities.values())[:10]:
100
+ cve_count = len(software["cves"])
101
+ unique_cves = list(set(software["cves"]))
102
+ cve_list = ", ".join(unique_cves[:5]) # Show first 5 CVEs
103
+ if len(unique_cves) > 5:
104
+ cve_list += f".. (+{len(unique_cves) - 5} more)"
105
+
106
+ details.append(
107
+ f"{software['name']} {software['version']} ({software['vendor']}) - "
108
+ f"{cve_count} weaknesses ({cve_list})"
109
+ )
110
+
111
+ # Add paths (limit to 4)
112
+ for path in list(software["paths"])[:4]:
113
+ details.append(f" - {path}")
114
+
115
+ # Determine the value based on severity:
116
+ # - Vulnerable software triggers warnings
117
+ # - No vulnerabilities is OK
118
+ if vulnerable_software:
119
+ value = len(vulnerable_software) # Will trigger warning threshold
120
+ else:
121
+ value = 0 # OK status
122
+
123
+ result = {
124
+ "value": value,
125
+ "details": details,
126
+ "vulnerable_count": len(vulnerable_software),
127
+ "total_cves": len(products),
128
+ "total_software": len(software_vulnerabilities),
129
+ }
130
+
131
+ self.logger.info(
132
+ f"Products analysis complete: {len(products)} total CVEs, "
133
+ f"{len(vulnerable_software)} vulnerable software"
134
+ )
135
+ self.logger.method_exit("get_result", result)
136
+ return result
@@ -41,9 +41,7 @@ class VulnerabilitiesService:
41
41
 
42
42
  # Process and deduplicate vulnerabilities
43
43
  vulnerabilities = self._process_vulnerabilities(raw_vulnerabilities)
44
- self.logger.info(
45
- f"Found {len(vulnerabilities)} unique vulnerabilities after deduplication"
46
- )
44
+ self.logger.info(f"Found {len(vulnerabilities)} unique vulnerabilities after deduplication")
47
45
 
48
46
  # Calculate vulnerability score
49
47
  score = VulnerabilityScore()
@@ -56,9 +54,7 @@ class VulnerabilitiesService:
56
54
 
57
55
  for vuln in sorted_vulnerabilities:
58
56
  severity = vuln.severity.lower()
59
- self.logger.debug(
60
- f"Processing vulnerability {vuln.id} with severity: {severity}"
61
- )
57
+ self.logger.debug(f"Processing vulnerability {vuln.id} with severity: {severity}")
62
58
 
63
59
  if severity == "critical":
64
60
  score.critical += 1
@@ -130,9 +126,7 @@ class VulnerabilitiesService:
130
126
  # Sort by severity
131
127
  sorted_vulnerabilities = self._sort_by_severity(vulnerabilities)
132
128
 
133
- self.logger.method_exit(
134
- "get_detailed_vulnerabilities", len(sorted_vulnerabilities)
135
- )
129
+ self.logger.method_exit("get_detailed_vulnerabilities", len(sorted_vulnerabilities))
136
130
  return sorted_vulnerabilities
137
131
 
138
132
  def _process_vulnerabilities(
@@ -163,9 +157,7 @@ class VulnerabilitiesService:
163
157
 
164
158
  return unique_vulnerabilities
165
159
 
166
- def _sort_by_severity(
167
- self, vulnerabilities: List[Vulnerability]
168
- ) -> List[Vulnerability]:
160
+ def _sort_by_severity(self, vulnerabilities: List[Vulnerability]) -> List[Vulnerability]:
169
161
  """Sort vulnerabilities by severity (Critical > High > Medium > Low)."""
170
162
  return sorted(
171
163
  vulnerabilities,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: check-msdefender
3
- Version: 1.1.6
3
+ Version: 1.1.7
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>
@@ -0,0 +1,36 @@
1
+ check_msdefender-1.1.7.dist-info/METADATA,sha256=pbEDMM_hakd-VnlkL04YdJ5zwDEfXVi9jRVmnpOpg50,13659
2
+ check_msdefender-1.1.7.dist-info/WHEEL,sha256=9P2ygRxDrTJz3gsagc0Z96ukrxjr-LFBGOgv3AuKlCA,90
3
+ check_msdefender-1.1.7.dist-info/entry_points.txt,sha256=OqVzHI1PaD9V22g0K7BhA2nYv4O-pH8mcLzuGdsk5rM,79
4
+ check_msdefender-1.1.7.dist-info/licenses/LICENSE,sha256=kW3DwIsKc9HVYdS4f4tI6sLo-EPqBQbz-WmuvHU4Nak,1065
5
+ check_msdefender/__init__.py,sha256=KubUPejwS0KpZHIggo-L59odnJWhnA4QX6uVxSFL-BU,160
6
+ check_msdefender/__main__.py,sha256=TuNsRSdnkQm9OdBTAwD5aB2zV_Irc50WgylVWhrfnLY,124
7
+ check_msdefender/check_msdefender.py,sha256=OO4Tg2DBW28AT-2LOH-qJM2pE5TPcF615BF7HjyZsmA,137
8
+ check_msdefender/cli/__init__.py,sha256=NWaS5ZI9_252AcReugF_WGPMOvQ_B7sC_s3pSrGujcI,291
9
+ check_msdefender/cli/__main__.py,sha256=TuNsRSdnkQm9OdBTAwD5aB2zV_Irc50WgylVWhrfnLY,124
10
+ check_msdefender/cli/commands/__init__.py,sha256=9VGUDUgBlVdkkBgkYvEKcr4qK8wtG9mGXTw-SszNEt8,822
11
+ check_msdefender/cli/commands/alerts.py,sha256=bC3nSF1iE83hOjwJdrL76yBpH4ovnWYNUZTnMzoIdIw,1821
12
+ check_msdefender/cli/commands/detail.py,sha256=qCATgEo_au7t93usEqyWAer6jYlHktQ7D_6iaeLvg3E,2284
13
+ check_msdefender/cli/commands/lastseen.py,sha256=my-kW00ioaFdmec3zjqrLk12kt9Pld8rqu5n8wcT4Ys,1878
14
+ check_msdefender/cli/commands/machines.py,sha256=uyQal7P4VI4a3dECFWgXKBiUPcdxhUrpWFOyKHmpORU,1724
15
+ check_msdefender/cli/commands/onboarding.py,sha256=5QSP75uyrX0MQ1ABiGFSDKIzVszLF8U3uQ4bqFF9F2g,1912
16
+ check_msdefender/cli/commands/products.py,sha256=negey5uZIKcZIzpEKwV-IZ8QLu6_sWLC6ffWZ9We4F4,1853
17
+ check_msdefender/cli/commands/vulnerabilities.py,sha256=CIYjANeMfcs20Ayi75cJpY98mjljH-DSujxc0E10L90,1931
18
+ check_msdefender/cli/decorators.py,sha256=wRUv4vY6SL3nFjpYW9h1M1xDO_pzA6--gCtg3y6MmQM,786
19
+ check_msdefender/cli/handlers.py,sha256=hp_CX_3qPoQGrPPVeiojb2j7tuFMva4ebWg9CxVUiPg,1395
20
+ check_msdefender/core/__init__.py,sha256=naBiEkixiWTuHU3GENk8fqC8H3p_hkzRsmSY2uiM_TQ,47
21
+ check_msdefender/core/auth.py,sha256=7mkGmhGHy4t38O0e4Rz7dQ52xfMbK3IUXMlw3u83aB4,1585
22
+ check_msdefender/core/config.py,sha256=IoWBL_DB110F4i6hFfli6iFDBXx57dHh32lCuLkcgNk,1170
23
+ check_msdefender/core/defender.py,sha256=Pf28KuRRubCWoATRW4-6K07A6WF6Mvf0XfO7Tfo13Yc,10423
24
+ check_msdefender/core/exceptions.py,sha256=X4s_XM64SEVSs-4mGKqnF8xXwGFY3E0buvkgRNuCCX4,600
25
+ check_msdefender/core/logging_config.py,sha256=Rd1F-IDXTx7yckrI8kyx2Ht20f5OcArPCAXb44BOmbg,4084
26
+ check_msdefender/core/nagios.py,sha256=5GY4MIFOOB_bVSTbESxCpNVkJg1zzuuNna6rlwsECvQ,6312
27
+ check_msdefender/services/__init__.py,sha256=_fiKXxcz263IghXn9BnUWDKPgedhUPoSakEN3tBd2SU,44
28
+ check_msdefender/services/alerts_service.py,sha256=poKZw1WKphmtPPnuMDrGRuPQbRLjLDZpo2rhFCh7TDc,4034
29
+ check_msdefender/services/detail_service.py,sha256=tXfb6H2dhrTZ5y85H8W58GA8CvA-7aUwMIbNdcqECw0,3381
30
+ check_msdefender/services/lastseen_service.py,sha256=LiNVeUbAoMzowMvE90P7zCtKFHBLbIDp5mmkVHRLwqs,3128
31
+ check_msdefender/services/machines_service.py,sha256=8JpiyAifddgSP1rpDvtzblRju7qUjAlAH6ej-18xX-k,3148
32
+ check_msdefender/services/models.py,sha256=CDmQ5vU0-GawIalqXjXNk3rry6gsyjv6eSlW2NiXwQ0,979
33
+ check_msdefender/services/onboarding_service.py,sha256=RIOsvALCoKV0YqnCHKYRkelSPrO-F-6vNBLlto4MpiI,2686
34
+ check_msdefender/services/products_service.py,sha256=GBwOEIsc1NP3KXCVhqmDIvkmKysDyvCXaFzXKnDnges,5662
35
+ check_msdefender/services/vulnerabilities_service.py,sha256=LuRRQlFt-K82tGUhLCx_QCOp4CbBgSp7fktmeSSoa9o,6838
36
+ check_msdefender-1.1.7.dist-info/RECORD,,
@@ -1,34 +0,0 @@
1
- check_msdefender-1.1.6.dist-info/METADATA,sha256=qT_sPso2g14fD6BseReTvSc_z7eKAhgOdM6nACYOQGw,13659
2
- check_msdefender-1.1.6.dist-info/WHEEL,sha256=9P2ygRxDrTJz3gsagc0Z96ukrxjr-LFBGOgv3AuKlCA,90
3
- check_msdefender-1.1.6.dist-info/entry_points.txt,sha256=OqVzHI1PaD9V22g0K7BhA2nYv4O-pH8mcLzuGdsk5rM,79
4
- check_msdefender-1.1.6.dist-info/licenses/LICENSE,sha256=kW3DwIsKc9HVYdS4f4tI6sLo-EPqBQbz-WmuvHU4Nak,1065
5
- check_msdefender/__init__.py,sha256=HDn1Ub7Ohqkfko0kUPT8w7HqU52jXwo-leSTImO1x_k,161
6
- check_msdefender/__main__.py,sha256=TuNsRSdnkQm9OdBTAwD5aB2zV_Irc50WgylVWhrfnLY,124
7
- check_msdefender/check_msdefender.py,sha256=OO4Tg2DBW28AT-2LOH-qJM2pE5TPcF615BF7HjyZsmA,137
8
- check_msdefender/cli/__init__.py,sha256=NWaS5ZI9_252AcReugF_WGPMOvQ_B7sC_s3pSrGujcI,291
9
- check_msdefender/cli/__main__.py,sha256=TuNsRSdnkQm9OdBTAwD5aB2zV_Irc50WgylVWhrfnLY,124
10
- check_msdefender/cli/commands/__init__.py,sha256=VkPrCVpBK3DnhVDZuwd_4uK69JYAE9rHZBjAZhyOBII,730
11
- check_msdefender/cli/commands/alerts.py,sha256=bC3nSF1iE83hOjwJdrL76yBpH4ovnWYNUZTnMzoIdIw,1821
12
- check_msdefender/cli/commands/detail.py,sha256=qCATgEo_au7t93usEqyWAer6jYlHktQ7D_6iaeLvg3E,2284
13
- check_msdefender/cli/commands/lastseen.py,sha256=my-kW00ioaFdmec3zjqrLk12kt9Pld8rqu5n8wcT4Ys,1878
14
- check_msdefender/cli/commands/machines.py,sha256=uyQal7P4VI4a3dECFWgXKBiUPcdxhUrpWFOyKHmpORU,1724
15
- check_msdefender/cli/commands/onboarding.py,sha256=5QSP75uyrX0MQ1ABiGFSDKIzVszLF8U3uQ4bqFF9F2g,1912
16
- check_msdefender/cli/commands/vulnerabilities.py,sha256=CIYjANeMfcs20Ayi75cJpY98mjljH-DSujxc0E10L90,1931
17
- check_msdefender/cli/decorators.py,sha256=EWgchw56-_ZIYEdwZNZsc5LncSw6obFp1RXKGTknU2E,800
18
- check_msdefender/cli/handlers.py,sha256=hp_CX_3qPoQGrPPVeiojb2j7tuFMva4ebWg9CxVUiPg,1395
19
- check_msdefender/core/__init__.py,sha256=naBiEkixiWTuHU3GENk8fqC8H3p_hkzRsmSY2uiM_TQ,47
20
- check_msdefender/core/auth.py,sha256=TDolCrwQoo37poaNUYJC2kgp4QpeH0W9AOathYCIUgw,1607
21
- check_msdefender/core/config.py,sha256=IoWBL_DB110F4i6hFfli6iFDBXx57dHh32lCuLkcgNk,1170
22
- check_msdefender/core/defender.py,sha256=mBbQ2-quDwUDooGPnl-SP91wzD_lHIM6BBZFl5bvXPA,8936
23
- check_msdefender/core/exceptions.py,sha256=X4s_XM64SEVSs-4mGKqnF8xXwGFY3E0buvkgRNuCCX4,600
24
- check_msdefender/core/logging_config.py,sha256=J4vdxbJBM4LDpbNvjRsVPZcsvW0yfRz7TnkJmOwyjpE,4122
25
- check_msdefender/core/nagios.py,sha256=uIp_WRkr-27gyjL1YKgzMhfcBmJt-0aSbm5XjtYyAN8,6632
26
- check_msdefender/services/__init__.py,sha256=_fiKXxcz263IghXn9BnUWDKPgedhUPoSakEN3tBd2SU,44
27
- check_msdefender/services/alerts_service.py,sha256=XxtP31Zt0WcfkW65YlJgoSVRxRX3yAJ4LTpgx9er-xc,4162
28
- check_msdefender/services/detail_service.py,sha256=PMoBv_m0udwWdPus5SvDx49V6iP9ERGWg5pX5cGARvA,3472
29
- check_msdefender/services/lastseen_service.py,sha256=T98Axx4ziUEavibXuG5FB2eHTyAcxktNM1DI4Y5fo1c,3158
30
- check_msdefender/services/machines_service.py,sha256=XYWnAliNo1DyIvdQ5xcPh133_CaW8U39WGdmiATwZps,3166
31
- check_msdefender/services/models.py,sha256=CDmQ5vU0-GawIalqXjXNk3rry6gsyjv6eSlW2NiXwQ0,979
32
- check_msdefender/services/onboarding_service.py,sha256=5ekrDlt6Vc__28O7b8niY-KPA14Dnw0UbuSgdx7_Lyg,2708
33
- check_msdefender/services/vulnerabilities_service.py,sha256=DfU6NaAkm-X5BVL3Zfm3lDe21vE-r1ErYh3r8nZLA6I,6926
34
- check_msdefender-1.1.6.dist-info/RECORD,,