check-msdefender 1.1.2__py3-none-any.whl → 1.1.4__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.
- check_msdefender/cli/decorators.py +3 -1
- check_msdefender/core/auth.py +3 -1
- check_msdefender/core/defender.py +19 -9
- check_msdefender/core/logging_config.py +5 -2
- check_msdefender/core/nagios.py +3 -1
- check_msdefender/services/alerts_service.py +15 -5
- check_msdefender/services/detail_service.py +9 -3
- check_msdefender/services/lastseen_service.py +3 -1
- check_msdefender/services/machines_service.py +9 -2
- check_msdefender/services/onboarding_service.py +3 -1
- check_msdefender/services/vulnerabilities_service.py +16 -6
- {check_msdefender-1.1.2.dist-info → check_msdefender-1.1.4.dist-info}/METADATA +2 -2
- {check_msdefender-1.1.2.dist-info → check_msdefender-1.1.4.dist-info}/RECORD +16 -16
- {check_msdefender-1.1.2.dist-info → check_msdefender-1.1.4.dist-info}/WHEEL +0 -0
- {check_msdefender-1.1.2.dist-info → check_msdefender-1.1.4.dist-info}/entry_points.txt +0 -0
- {check_msdefender-1.1.2.dist-info → check_msdefender-1.1.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -10,7 +10,9 @@ 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)")(
|
|
13
|
+
func = click.option("-m", "--machine-id", "-i", "--id", help="Machine ID (GUID)")(
|
|
14
|
+
func
|
|
15
|
+
)
|
|
14
16
|
func = click.option("-d", "--dns-name", help="Computer DNS Name (FQDN)")(func)
|
|
15
17
|
func = click.option("-W", "--warning", type=float, help="Warning threshold")(func)
|
|
16
18
|
func = click.option("-C", "--critical", type=float, help="Critical threshold")(func)
|
check_msdefender/core/auth.py
CHANGED
|
@@ -20,7 +20,9 @@ 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(
|
|
23
|
+
raise ConfigurationError(
|
|
24
|
+
"client_id and tenant_id are required in [auth] section"
|
|
25
|
+
)
|
|
24
26
|
|
|
25
27
|
# Check for client secret authentication
|
|
26
28
|
client_secret = auth_section.get("client_secret")
|
|
@@ -13,7 +13,11 @@ class DefenderClient:
|
|
|
13
13
|
application_json = "application/json"
|
|
14
14
|
|
|
15
15
|
def __init__(
|
|
16
|
-
self,
|
|
16
|
+
self,
|
|
17
|
+
authenticator: Any,
|
|
18
|
+
timeout: int = 5,
|
|
19
|
+
region: str = "eu",
|
|
20
|
+
verbose_level: int = 0,
|
|
17
21
|
) -> None:
|
|
18
22
|
"""Initialize with authenticator and optional region.
|
|
19
23
|
|
|
@@ -32,12 +36,12 @@ class DefenderClient:
|
|
|
32
36
|
def _get_base_url(self, region: str) -> str:
|
|
33
37
|
"""Get base URL for the specified region."""
|
|
34
38
|
endpoints = {
|
|
35
|
-
"eu": "https://
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
+
"eu": "https://eu.api.security.microsoft.com",
|
|
40
|
+
"us": "https://us.api.security.microsoft.com",
|
|
41
|
+
"uk": "https://uk.api.security.microsoft.com",
|
|
42
|
+
"api": "https://api.security.microsoft.com",
|
|
39
43
|
}
|
|
40
|
-
return endpoints.get(region, endpoints["
|
|
44
|
+
return endpoints.get(region, endpoints["eu"])
|
|
41
45
|
|
|
42
46
|
def get_machine_by_dns_name(self, dns_name: str) -> Dict[str, Any]:
|
|
43
47
|
"""Get machine information by DNS name."""
|
|
@@ -56,7 +60,9 @@ class DefenderClient:
|
|
|
56
60
|
try:
|
|
57
61
|
start_time = time.time()
|
|
58
62
|
self.logger.info(f"Querying machine by DNS name: {dns_name}")
|
|
59
|
-
response = requests.get(
|
|
63
|
+
response = requests.get(
|
|
64
|
+
url, headers=headers, params=params, timeout=self.timeout
|
|
65
|
+
)
|
|
60
66
|
elapsed = time.time() - start_time
|
|
61
67
|
|
|
62
68
|
self.logger.api_call("GET", url, response.status_code, elapsed)
|
|
@@ -151,7 +157,9 @@ class DefenderClient:
|
|
|
151
157
|
try:
|
|
152
158
|
start_time = time.time()
|
|
153
159
|
self.logger.info("Querying all machines")
|
|
154
|
-
response = requests.get(
|
|
160
|
+
response = requests.get(
|
|
161
|
+
url, headers=headers, params=params, timeout=self.timeout
|
|
162
|
+
)
|
|
155
163
|
elapsed = time.time() - start_time
|
|
156
164
|
|
|
157
165
|
self.logger.api_call("GET", url, response.status_code, elapsed)
|
|
@@ -189,7 +197,9 @@ class DefenderClient:
|
|
|
189
197
|
try:
|
|
190
198
|
start_time = time.time()
|
|
191
199
|
self.logger.info("Querying alerts")
|
|
192
|
-
response = requests.get(
|
|
200
|
+
response = requests.get(
|
|
201
|
+
url, headers=headers, params=params, timeout=self.timeout
|
|
202
|
+
)
|
|
193
203
|
elapsed = time.time() - start_time
|
|
194
204
|
|
|
195
205
|
self.logger.api_call("GET", url, response.status_code, elapsed)
|
|
@@ -36,7 +36,8 @@ class VerboseLogger:
|
|
|
36
36
|
if self.verbose_level >= 3:
|
|
37
37
|
# Full trace format
|
|
38
38
|
formatter = logging.Formatter(
|
|
39
|
-
"[%(levelname)s] %(asctime)s %(name)s:%(lineno)d - %(message)s",
|
|
39
|
+
"[%(levelname)s] %(asctime)s %(name)s:%(lineno)d - %(message)s",
|
|
40
|
+
datefmt="%H:%M:%S",
|
|
40
41
|
)
|
|
41
42
|
elif self.verbose_level >= 2:
|
|
42
43
|
# Debug format
|
|
@@ -81,7 +82,9 @@ class VerboseLogger:
|
|
|
81
82
|
"""Log API call details if verbose >= 2."""
|
|
82
83
|
if self.verbose_level >= 2:
|
|
83
84
|
if status_code and response_time:
|
|
84
|
-
self.logger.debug(
|
|
85
|
+
self.logger.debug(
|
|
86
|
+
f"API {method} {url} -> {status_code} ({response_time:.3f}s)"
|
|
87
|
+
)
|
|
85
88
|
else:
|
|
86
89
|
self.logger.debug(f"API {method} {url}")
|
|
87
90
|
|
check_msdefender/core/nagios.py
CHANGED
|
@@ -128,7 +128,9 @@ class NagiosPlugin:
|
|
|
128
128
|
|
|
129
129
|
# Create Nagios check with custom summary
|
|
130
130
|
# Use 'found' as context name for detail command, otherwise use command name
|
|
131
|
-
context_name =
|
|
131
|
+
context_name = (
|
|
132
|
+
"found" if self.command_name == "detail" else self.command_name
|
|
133
|
+
)
|
|
132
134
|
check = nagiosplugin.Check(
|
|
133
135
|
DefenderResource(self.command_name, value),
|
|
134
136
|
DefenderScalarContext(context_name, warning, critical),
|
|
@@ -53,12 +53,18 @@ class AlertsService:
|
|
|
53
53
|
or alert.get("computerDnsName") == target_dns_name
|
|
54
54
|
]
|
|
55
55
|
|
|
56
|
-
self.logger.info(
|
|
56
|
+
self.logger.info(
|
|
57
|
+
f"Found {len(machine_alerts)} alerts for machine {target_dns_name}"
|
|
58
|
+
)
|
|
57
59
|
|
|
58
60
|
# Categorize alerts by status and severity
|
|
59
|
-
unresolved_alerts = [
|
|
61
|
+
unresolved_alerts = [
|
|
62
|
+
alert for alert in machine_alerts if alert.get("status") != "Resolved"
|
|
63
|
+
]
|
|
60
64
|
informational_alerts = [
|
|
61
|
-
alert
|
|
65
|
+
alert
|
|
66
|
+
for alert in unresolved_alerts
|
|
67
|
+
if alert.get("severity") == "Informational"
|
|
62
68
|
]
|
|
63
69
|
critical_warning_alerts = [
|
|
64
70
|
alert
|
|
@@ -82,7 +88,9 @@ class AlertsService:
|
|
|
82
88
|
title = alert.get("title", "Unknown alert")
|
|
83
89
|
status = alert.get("status", "Unknown")
|
|
84
90
|
severity = alert.get("severity", "Unknown")
|
|
85
|
-
details.append(
|
|
91
|
+
details.append(
|
|
92
|
+
f"{creation_time} - {title} ({status} {severity.lower()})"
|
|
93
|
+
)
|
|
86
94
|
|
|
87
95
|
# Return the number of unresolved alerts as the value
|
|
88
96
|
# This will be used by Nagios plugin for determining status based on thresholds
|
|
@@ -93,6 +101,8 @@ class AlertsService:
|
|
|
93
101
|
"details": details,
|
|
94
102
|
}
|
|
95
103
|
|
|
96
|
-
self.logger.info(
|
|
104
|
+
self.logger.info(
|
|
105
|
+
f"Alert analysis complete: {len(unresolved_alerts)} unresolved alerts"
|
|
106
|
+
)
|
|
97
107
|
self.logger.method_exit("get_result", result)
|
|
98
108
|
return result
|
|
@@ -54,10 +54,16 @@ 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
|
-
|
|
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
|
+
)
|
|
59
63
|
details.append(f"OS Version: {machine_details.get('osVersion', 'Unknown')}")
|
|
60
|
-
details.append(
|
|
64
|
+
details.append(
|
|
65
|
+
f"Health Status: {machine_details.get('healthStatus', 'Unknown')}"
|
|
66
|
+
)
|
|
61
67
|
details.append(f"Risk Score: {machine_details.get('riskScore', 'Unknown')}")
|
|
62
68
|
|
|
63
69
|
result = {"value": 1, "details": details}
|
|
@@ -62,7 +62,9 @@ class LastSeenService:
|
|
|
62
62
|
|
|
63
63
|
result = {"value": days_diff, "details": details}
|
|
64
64
|
|
|
65
|
-
self.logger.info(
|
|
65
|
+
self.logger.info(
|
|
66
|
+
f"Machine last seen {days_diff} days ago ({last_seen_str})"
|
|
67
|
+
)
|
|
66
68
|
self.logger.method_exit("get_result", result)
|
|
67
69
|
return result
|
|
68
70
|
except (ValueError, TypeError) as e:
|
|
@@ -23,7 +23,10 @@ class MachinesService:
|
|
|
23
23
|
|
|
24
24
|
if not machines_data.get("value"):
|
|
25
25
|
self.logger.info("No machines found")
|
|
26
|
-
result = {
|
|
26
|
+
result = {
|
|
27
|
+
"value": 0,
|
|
28
|
+
"details": ["No machines found in Microsoft Defender"],
|
|
29
|
+
}
|
|
27
30
|
self.logger.method_exit("get_result", result)
|
|
28
31
|
return result
|
|
29
32
|
|
|
@@ -39,7 +42,11 @@ class MachinesService:
|
|
|
39
42
|
|
|
40
43
|
# Sort by priority
|
|
41
44
|
sorted_machines = sorted(
|
|
42
|
-
machines,
|
|
45
|
+
machines,
|
|
46
|
+
key=lambda x: (
|
|
47
|
+
status_priority[x["onboardingStatus"]],
|
|
48
|
+
x["computerDnsName"],
|
|
49
|
+
),
|
|
43
50
|
)
|
|
44
51
|
for machine in sorted_machines:
|
|
45
52
|
onboarded = "✓" if machine["onboardingStatus"] == "Onboarded" else "✗"
|
|
@@ -54,6 +54,8 @@ class OnboardingService:
|
|
|
54
54
|
|
|
55
55
|
result = {"value": result_value, "details": details}
|
|
56
56
|
|
|
57
|
-
self.logger.info(
|
|
57
|
+
self.logger.info(
|
|
58
|
+
f"Machine onboarding status: {onboarding_state} -> {result_value}"
|
|
59
|
+
)
|
|
58
60
|
self.logger.method_exit("get_result", result)
|
|
59
61
|
return result
|
|
@@ -41,7 +41,9 @@ class VulnerabilitiesService:
|
|
|
41
41
|
|
|
42
42
|
# Process and deduplicate vulnerabilities
|
|
43
43
|
vulnerabilities = self._process_vulnerabilities(raw_vulnerabilities)
|
|
44
|
-
self.logger.info(
|
|
44
|
+
self.logger.info(
|
|
45
|
+
f"Found {len(vulnerabilities)} unique vulnerabilities after deduplication"
|
|
46
|
+
)
|
|
45
47
|
|
|
46
48
|
# Calculate vulnerability score
|
|
47
49
|
score = VulnerabilityScore()
|
|
@@ -54,7 +56,9 @@ class VulnerabilitiesService:
|
|
|
54
56
|
|
|
55
57
|
for vuln in sorted_vulnerabilities:
|
|
56
58
|
severity = vuln.severity.lower()
|
|
57
|
-
self.logger.debug(
|
|
59
|
+
self.logger.debug(
|
|
60
|
+
f"Processing vulnerability {vuln.id} with severity: {severity}"
|
|
61
|
+
)
|
|
58
62
|
|
|
59
63
|
if severity == "critical":
|
|
60
64
|
score.critical += 1
|
|
@@ -75,7 +79,8 @@ class VulnerabilitiesService:
|
|
|
75
79
|
self.logger.info(f"Total vulnerability score: {score.total_score}")
|
|
76
80
|
|
|
77
81
|
details.insert(
|
|
78
|
-
0,
|
|
82
|
+
0,
|
|
83
|
+
f"Vulnerabilities: {len(raw_vulnerabilities)}, score: {score.total_score}",
|
|
79
84
|
)
|
|
80
85
|
|
|
81
86
|
result = {"value": score.total_score, "details": details}
|
|
@@ -125,7 +130,9 @@ class VulnerabilitiesService:
|
|
|
125
130
|
# Sort by severity
|
|
126
131
|
sorted_vulnerabilities = self._sort_by_severity(vulnerabilities)
|
|
127
132
|
|
|
128
|
-
self.logger.method_exit(
|
|
133
|
+
self.logger.method_exit(
|
|
134
|
+
"get_detailed_vulnerabilities", len(sorted_vulnerabilities)
|
|
135
|
+
)
|
|
129
136
|
return sorted_vulnerabilities
|
|
130
137
|
|
|
131
138
|
def _process_vulnerabilities(
|
|
@@ -156,8 +163,11 @@ class VulnerabilitiesService:
|
|
|
156
163
|
|
|
157
164
|
return unique_vulnerabilities
|
|
158
165
|
|
|
159
|
-
def _sort_by_severity(
|
|
166
|
+
def _sort_by_severity(
|
|
167
|
+
self, vulnerabilities: List[Vulnerability]
|
|
168
|
+
) -> List[Vulnerability]:
|
|
160
169
|
"""Sort vulnerabilities by severity (Critical > High > Medium > Low)."""
|
|
161
170
|
return sorted(
|
|
162
|
-
vulnerabilities,
|
|
171
|
+
vulnerabilities,
|
|
172
|
+
key=lambda v: self._severity_order.get(v.severity.lower(), 999),
|
|
163
173
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: check-msdefender
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.4
|
|
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>
|
|
@@ -32,6 +32,7 @@ Requires-Dist: flake8>=3.8; extra == "dev"
|
|
|
32
32
|
Requires-Dist: mypy>=0.800; extra == "dev"
|
|
33
33
|
Requires-Dist: twine>=6.2.0; extra == "dev"
|
|
34
34
|
Requires-Dist: pdm>=2.0.0; extra == "dev"
|
|
35
|
+
Requires-Dist: ruff>=0.13.0; extra == "dev"
|
|
35
36
|
Description-Content-Type: text/markdown
|
|
36
37
|
|
|
37
38
|
# 🛡️ Check MS Defender
|
|
@@ -294,7 +295,6 @@ source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
|
294
295
|
|
|
295
296
|
# Install in development mode
|
|
296
297
|
pip install -e .
|
|
297
|
-
pip install -r requirements-dev.txt
|
|
298
298
|
```
|
|
299
299
|
|
|
300
300
|
### Code Quality Tools
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
check_msdefender-1.1.
|
|
2
|
-
check_msdefender-1.1.
|
|
3
|
-
check_msdefender-1.1.
|
|
4
|
-
check_msdefender-1.1.
|
|
1
|
+
check_msdefender-1.1.4.dist-info/METADATA,sha256=XGMBO_Ya5rReVoyq3pbxitdBA4S2J1yC_MF9S1XBb_Y,14079
|
|
2
|
+
check_msdefender-1.1.4.dist-info/WHEEL,sha256=9P2ygRxDrTJz3gsagc0Z96ukrxjr-LFBGOgv3AuKlCA,90
|
|
3
|
+
check_msdefender-1.1.4.dist-info/entry_points.txt,sha256=OqVzHI1PaD9V22g0K7BhA2nYv4O-pH8mcLzuGdsk5rM,79
|
|
4
|
+
check_msdefender-1.1.4.dist-info/licenses/LICENSE,sha256=kW3DwIsKc9HVYdS4f4tI6sLo-EPqBQbz-WmuvHU4Nak,1065
|
|
5
5
|
check_msdefender/__init__.py,sha256=HDn1Ub7Ohqkfko0kUPT8w7HqU52jXwo-leSTImO1x_k,161
|
|
6
6
|
check_msdefender/__main__.py,sha256=TuNsRSdnkQm9OdBTAwD5aB2zV_Irc50WgylVWhrfnLY,124
|
|
7
7
|
check_msdefender/check_msdefender.py,sha256=OO4Tg2DBW28AT-2LOH-qJM2pE5TPcF615BF7HjyZsmA,137
|
|
@@ -14,21 +14,21 @@ check_msdefender/cli/commands/lastseen.py,sha256=my-kW00ioaFdmec3zjqrLk12kt9Pld8
|
|
|
14
14
|
check_msdefender/cli/commands/machines.py,sha256=uyQal7P4VI4a3dECFWgXKBiUPcdxhUrpWFOyKHmpORU,1724
|
|
15
15
|
check_msdefender/cli/commands/onboarding.py,sha256=5QSP75uyrX0MQ1ABiGFSDKIzVszLF8U3uQ4bqFF9F2g,1912
|
|
16
16
|
check_msdefender/cli/commands/vulnerabilities.py,sha256=fl8NYAO4Ug2Yk5NmQ6CT9TuibhFHWE01R6YjDIPgYl4,1931
|
|
17
|
-
check_msdefender/cli/decorators.py,sha256=
|
|
17
|
+
check_msdefender/cli/decorators.py,sha256=EWgchw56-_ZIYEdwZNZsc5LncSw6obFp1RXKGTknU2E,800
|
|
18
18
|
check_msdefender/cli/handlers.py,sha256=hp_CX_3qPoQGrPPVeiojb2j7tuFMva4ebWg9CxVUiPg,1395
|
|
19
19
|
check_msdefender/core/__init__.py,sha256=naBiEkixiWTuHU3GENk8fqC8H3p_hkzRsmSY2uiM_TQ,47
|
|
20
|
-
check_msdefender/core/auth.py,sha256=
|
|
20
|
+
check_msdefender/core/auth.py,sha256=TDolCrwQoo37poaNUYJC2kgp4QpeH0W9AOathYCIUgw,1607
|
|
21
21
|
check_msdefender/core/config.py,sha256=IoWBL_DB110F4i6hFfli6iFDBXx57dHh32lCuLkcgNk,1170
|
|
22
|
-
check_msdefender/core/defender.py,sha256=
|
|
22
|
+
check_msdefender/core/defender.py,sha256=bP0oB_Y9uu1Rx6gZeYc6KDJDcrmUQdgPaHRaswHXa5U,8934
|
|
23
23
|
check_msdefender/core/exceptions.py,sha256=X4s_XM64SEVSs-4mGKqnF8xXwGFY3E0buvkgRNuCCX4,600
|
|
24
|
-
check_msdefender/core/logging_config.py,sha256=
|
|
25
|
-
check_msdefender/core/nagios.py,sha256=
|
|
24
|
+
check_msdefender/core/logging_config.py,sha256=J4vdxbJBM4LDpbNvjRsVPZcsvW0yfRz7TnkJmOwyjpE,4122
|
|
25
|
+
check_msdefender/core/nagios.py,sha256=uIp_WRkr-27gyjL1YKgzMhfcBmJt-0aSbm5XjtYyAN8,6632
|
|
26
26
|
check_msdefender/services/__init__.py,sha256=_fiKXxcz263IghXn9BnUWDKPgedhUPoSakEN3tBd2SU,44
|
|
27
|
-
check_msdefender/services/alerts_service.py,sha256=
|
|
28
|
-
check_msdefender/services/detail_service.py,sha256=
|
|
29
|
-
check_msdefender/services/lastseen_service.py,sha256=
|
|
30
|
-
check_msdefender/services/machines_service.py,sha256=
|
|
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
31
|
check_msdefender/services/models.py,sha256=CDmQ5vU0-GawIalqXjXNk3rry6gsyjv6eSlW2NiXwQ0,979
|
|
32
|
-
check_msdefender/services/onboarding_service.py,sha256=
|
|
33
|
-
check_msdefender/services/vulnerabilities_service.py,sha256=
|
|
34
|
-
check_msdefender-1.1.
|
|
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.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|