regscale-cli 6.25.0.1__py3-none-any.whl → 6.25.1.0__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.
Potentially problematic release.
This version of regscale-cli might be problematic. Click here for more details.
- regscale/_version.py +1 -1
- regscale/integrations/commercial/synqly/assets.py +17 -0
- regscale/integrations/commercial/wizv2/constants.py +20 -71
- regscale/integrations/due_date_handler.py +118 -6
- regscale/integrations/scanner_integration.py +49 -34
- regscale/models/integration_models/cisa_kev_data.json +48 -3
- regscale/models/integration_models/synqly_models/capabilities.json +1 -1
- regscale/utils/threading/threadhandler.py +20 -15
- {regscale_cli-6.25.0.1.dist-info → regscale_cli-6.25.1.0.dist-info}/METADATA +1 -1
- {regscale_cli-6.25.0.1.dist-info → regscale_cli-6.25.1.0.dist-info}/RECORD +14 -14
- {regscale_cli-6.25.0.1.dist-info → regscale_cli-6.25.1.0.dist-info}/LICENSE +0 -0
- {regscale_cli-6.25.0.1.dist-info → regscale_cli-6.25.1.0.dist-info}/WHEEL +0 -0
- {regscale_cli-6.25.0.1.dist-info → regscale_cli-6.25.1.0.dist-info}/entry_points.txt +0 -0
- {regscale_cli-6.25.0.1.dist-info → regscale_cli-6.25.1.0.dist-info}/top_level.txt +0 -0
regscale/_version.py
CHANGED
|
@@ -97,6 +97,23 @@ def sync_crowdstrike(regscale_ssp_id: int, filter: str, url: str) -> None:
|
|
|
97
97
|
assets_crowdstrike.run_sync(regscale_ssp_id=regscale_ssp_id, filter=filter.split(";") if filter else [], url=url)
|
|
98
98
|
|
|
99
99
|
|
|
100
|
+
@assets.command(name="sync_ivanti_neurons")
|
|
101
|
+
@regscale_ssp_id()
|
|
102
|
+
@click.option(
|
|
103
|
+
"--filter",
|
|
104
|
+
help='STRING: Apply filters to the query. Can be a single filter "field[operator]value" or semicolon-separated filters "field1[op]value1;field2[op]value2"',
|
|
105
|
+
required=False,
|
|
106
|
+
type=str,
|
|
107
|
+
default=None,
|
|
108
|
+
)
|
|
109
|
+
def sync_ivanti_neurons(regscale_ssp_id: int, filter: str) -> None:
|
|
110
|
+
"""Sync Assets from Ivanti Neurons to RegScale."""
|
|
111
|
+
from regscale.models.integration_models.synqly_models.connectors import Assets
|
|
112
|
+
|
|
113
|
+
assets_ivanti_neurons = Assets("ivanti_neurons")
|
|
114
|
+
assets_ivanti_neurons.run_sync(regscale_ssp_id=regscale_ssp_id, filter=filter.split(";") if filter else [])
|
|
115
|
+
|
|
116
|
+
|
|
100
117
|
@assets.command(name="sync_nozomi_vantage")
|
|
101
118
|
@regscale_ssp_id()
|
|
102
119
|
@click.option(
|
|
@@ -1171,85 +1171,24 @@ fragment SecuritySubCategoryDetails on SecuritySubCategory {
|
|
|
1171
1171
|
}
|
|
1172
1172
|
"""
|
|
1173
1173
|
DATA_FINDING_QUERY = """
|
|
1174
|
-
query
|
|
1175
|
-
|
|
1176
|
-
groupBy: $groupBy
|
|
1174
|
+
query DataFindingsTable($after: String, $first: Int, $filterBy: DataFindingFiltersV2, $orderBy: DataFindingOrder, $fetchTotalCount: Boolean = true) {
|
|
1175
|
+
dataFindingsV2(
|
|
1177
1176
|
filterBy: $filterBy
|
|
1178
1177
|
first: $first
|
|
1179
1178
|
after: $after
|
|
1180
1179
|
orderBy: $orderBy
|
|
1181
1180
|
) {
|
|
1182
1181
|
nodes {
|
|
1183
|
-
|
|
1184
|
-
location {
|
|
1185
|
-
countryCode
|
|
1186
|
-
state
|
|
1187
|
-
}
|
|
1188
|
-
regionCount
|
|
1189
|
-
graphEntityCount
|
|
1190
|
-
graphEntity {
|
|
1191
|
-
id
|
|
1192
|
-
name
|
|
1193
|
-
type
|
|
1194
|
-
properties
|
|
1195
|
-
projects {
|
|
1196
|
-
id
|
|
1197
|
-
name
|
|
1198
|
-
slug
|
|
1199
|
-
isFolder
|
|
1200
|
-
}
|
|
1201
|
-
issues(filterBy: {status: [OPEN, IN_PROGRESS]}) {
|
|
1202
|
-
criticalSeverityCount
|
|
1203
|
-
highSeverityCount
|
|
1204
|
-
mediumSeverityCount
|
|
1205
|
-
lowSeverityCount
|
|
1206
|
-
informationalSeverityCount
|
|
1207
|
-
}
|
|
1208
|
-
}
|
|
1209
|
-
cloudAccount {
|
|
1210
|
-
id
|
|
1211
|
-
name
|
|
1212
|
-
externalId
|
|
1213
|
-
cloudProvider
|
|
1214
|
-
}
|
|
1215
|
-
dataClassifiers {
|
|
1216
|
-
id
|
|
1217
|
-
name
|
|
1218
|
-
category
|
|
1219
|
-
matcherType
|
|
1220
|
-
severity
|
|
1221
|
-
}
|
|
1222
|
-
securitySubCategories {
|
|
1223
|
-
id
|
|
1224
|
-
title
|
|
1225
|
-
externalId
|
|
1226
|
-
description
|
|
1227
|
-
category {
|
|
1228
|
-
id
|
|
1229
|
-
name
|
|
1230
|
-
description
|
|
1231
|
-
framework {
|
|
1232
|
-
id
|
|
1233
|
-
name
|
|
1234
|
-
description
|
|
1235
|
-
enabled
|
|
1236
|
-
}
|
|
1237
|
-
}
|
|
1238
|
-
}
|
|
1239
|
-
findingsCount
|
|
1240
|
-
dataFindings(first: 5) {
|
|
1241
|
-
nodes {
|
|
1242
|
-
...DataFindingDetails
|
|
1243
|
-
}
|
|
1244
|
-
}
|
|
1182
|
+
...DataFindingDetails
|
|
1245
1183
|
}
|
|
1246
1184
|
pageInfo {
|
|
1247
1185
|
hasNextPage
|
|
1248
1186
|
endCursor
|
|
1249
1187
|
}
|
|
1250
|
-
totalCount
|
|
1188
|
+
totalCount @include(if: $fetchTotalCount)
|
|
1251
1189
|
}
|
|
1252
1190
|
}
|
|
1191
|
+
|
|
1253
1192
|
fragment DataFindingDetails on DataFinding {
|
|
1254
1193
|
id
|
|
1255
1194
|
name
|
|
@@ -1257,10 +1196,10 @@ fragment DataFindingDetails on DataFinding {
|
|
|
1257
1196
|
id
|
|
1258
1197
|
name
|
|
1259
1198
|
category
|
|
1199
|
+
isTenantSpecific
|
|
1260
1200
|
securitySubCategories {
|
|
1261
1201
|
id
|
|
1262
1202
|
title
|
|
1263
|
-
externalId
|
|
1264
1203
|
description
|
|
1265
1204
|
category {
|
|
1266
1205
|
id
|
|
@@ -1286,8 +1225,12 @@ fragment DataFindingDetails on DataFinding {
|
|
|
1286
1225
|
state
|
|
1287
1226
|
}
|
|
1288
1227
|
severity
|
|
1228
|
+
status
|
|
1289
1229
|
totalMatchCount
|
|
1290
1230
|
uniqueMatchCount
|
|
1231
|
+
maxUniqueMatchesReached
|
|
1232
|
+
uniqueLocationsCount
|
|
1233
|
+
isEntityPublic
|
|
1291
1234
|
graphEntity {
|
|
1292
1235
|
id
|
|
1293
1236
|
name
|
|
@@ -1301,6 +1244,12 @@ fragment DataFindingDetails on DataFinding {
|
|
|
1301
1244
|
}
|
|
1302
1245
|
}
|
|
1303
1246
|
externalSource
|
|
1247
|
+
details {
|
|
1248
|
+
applicationServices {
|
|
1249
|
+
id
|
|
1250
|
+
displayName
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1304
1253
|
}
|
|
1305
1254
|
"""
|
|
1306
1255
|
|
|
@@ -1387,14 +1336,14 @@ def get_wiz_vulnerability_queries(project_id: str, filter_by: Optional[dict] = N
|
|
|
1387
1336
|
{
|
|
1388
1337
|
"type": WizVulnerabilityType.DATA_FINDING,
|
|
1389
1338
|
"query": DATA_FINDING_QUERY,
|
|
1390
|
-
"topic_key": "
|
|
1339
|
+
"topic_key": "dataFindingsV2",
|
|
1391
1340
|
"file_path": DATA_FINDINGS_FILE_PATH,
|
|
1392
|
-
"asset_lookup": "
|
|
1341
|
+
"asset_lookup": "graphEntity",
|
|
1393
1342
|
"variables": {
|
|
1394
1343
|
"first": 200,
|
|
1344
|
+
"fetchTotalCount": True,
|
|
1395
1345
|
"filterBy": {"projectId": [project_id]},
|
|
1396
|
-
"orderBy": {"field": "
|
|
1397
|
-
"groupBy": "GRAPH_ENTITY",
|
|
1346
|
+
"orderBy": {"field": "TOTAL_MATCHES", "direction": "DESC"},
|
|
1398
1347
|
},
|
|
1399
1348
|
},
|
|
1400
1349
|
{
|
|
@@ -7,13 +7,16 @@ from datetime import datetime
|
|
|
7
7
|
from typing import Any, Dict, Optional
|
|
8
8
|
|
|
9
9
|
from regscale.core.app.application import Application
|
|
10
|
-
from regscale.core.utils.date import
|
|
10
|
+
from regscale.core.utils.date import get_day_increment
|
|
11
11
|
from regscale.integrations.public.cisa import pull_cisa_kev
|
|
12
12
|
from regscale.models import regscale_models
|
|
13
13
|
from regscale.utils.threading import ThreadSafeDict
|
|
14
14
|
|
|
15
15
|
logger = logging.getLogger("regscale")
|
|
16
16
|
|
|
17
|
+
# Date format constant for consistent datetime string formatting
|
|
18
|
+
DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%S"
|
|
19
|
+
|
|
17
20
|
|
|
18
21
|
class DueDateHandler:
|
|
19
22
|
"""
|
|
@@ -21,6 +24,19 @@ class DueDateHandler:
|
|
|
21
24
|
1. Init.yaml timeline configurations per integration
|
|
22
25
|
2. KEV (Known Exploited Vulnerabilities) dates from CISA
|
|
23
26
|
3. Default severity-based timelines
|
|
27
|
+
4. Configurable past due date validation (noPastDueDates setting)
|
|
28
|
+
|
|
29
|
+
Configuration Options:
|
|
30
|
+
- Global setting: issues.noPastDueDates (default: true)
|
|
31
|
+
- Per-integration: issues.{integration_name}.noPastDueDates
|
|
32
|
+
|
|
33
|
+
When noPastDueDates=true (default):
|
|
34
|
+
- Due dates calculated in the past are automatically adjusted to future dates
|
|
35
|
+
- Prevents API validation errors for past due dates
|
|
36
|
+
|
|
37
|
+
When noPastDueDates=false:
|
|
38
|
+
- Original due dates are preserved, even if they're in the past
|
|
39
|
+
- Useful for historical data or when past dates are intentionally required
|
|
24
40
|
"""
|
|
25
41
|
|
|
26
42
|
def __init__(self, integration_name: str, config: Optional[Dict[str, Any]] = None):
|
|
@@ -45,6 +61,9 @@ class DueDateHandler:
|
|
|
45
61
|
# Load integration-specific timelines from config
|
|
46
62
|
self.integration_timelines = self._load_integration_timelines()
|
|
47
63
|
|
|
64
|
+
# Load noPastDueDates setting (defaults to True)
|
|
65
|
+
self.no_past_due_dates = self._load_no_past_due_dates_setting()
|
|
66
|
+
|
|
48
67
|
def _load_integration_timelines(self) -> Dict[regscale_models.IssueSeverity, int]:
|
|
49
68
|
"""
|
|
50
69
|
Load timeline configurations for this integration from init.yaml
|
|
@@ -75,6 +94,37 @@ class DueDateHandler:
|
|
|
75
94
|
|
|
76
95
|
return timelines
|
|
77
96
|
|
|
97
|
+
def _load_no_past_due_dates_setting(self) -> bool:
|
|
98
|
+
"""
|
|
99
|
+
Load noPastDueDates setting for this integration from init.yaml
|
|
100
|
+
|
|
101
|
+
Configuration hierarchy:
|
|
102
|
+
1. Integration-specific setting: issues.{integration_name}.noPastDueDates
|
|
103
|
+
2. Global setting: issues.noPastDueDates
|
|
104
|
+
3. Default: True
|
|
105
|
+
|
|
106
|
+
:return: True if past due dates should be automatically adjusted to future dates
|
|
107
|
+
:rtype: bool
|
|
108
|
+
"""
|
|
109
|
+
issues_config = self.config.get("issues", {})
|
|
110
|
+
integration_config = issues_config.get(self.integration_name, {})
|
|
111
|
+
|
|
112
|
+
# Check integration-specific setting first
|
|
113
|
+
if "noPastDueDates" in integration_config:
|
|
114
|
+
setting = integration_config["noPastDueDates"]
|
|
115
|
+
logger.debug(f"Using integration-specific noPastDueDates={setting} for {self.integration_name}")
|
|
116
|
+
return bool(setting)
|
|
117
|
+
|
|
118
|
+
# Fall back to global setting
|
|
119
|
+
if "noPastDueDates" in issues_config:
|
|
120
|
+
setting = issues_config["noPastDueDates"]
|
|
121
|
+
logger.debug(f"Using global noPastDueDates={setting} for {self.integration_name}")
|
|
122
|
+
return bool(setting)
|
|
123
|
+
|
|
124
|
+
# Default to True (prevent past due dates)
|
|
125
|
+
logger.debug(f"Using default noPastDueDates=True for {self.integration_name}")
|
|
126
|
+
return True
|
|
127
|
+
|
|
78
128
|
def _get_kev_data(self) -> ThreadSafeDict:
|
|
79
129
|
"""
|
|
80
130
|
Get KEV data from CISA, using cache if available
|
|
@@ -160,17 +210,26 @@ class DueDateHandler:
|
|
|
160
210
|
kev_date = date_parse(kev_due_date).date()
|
|
161
211
|
created_dt = date_parse(created_date).date()
|
|
162
212
|
|
|
163
|
-
# If KEV due date is after creation date, use KEV date
|
|
213
|
+
# If KEV due date is after creation date, use KEV date but ensure it's not in the past
|
|
164
214
|
# If KEV due date is before creation date, add the difference to creation date
|
|
165
215
|
if kev_date >= created_dt:
|
|
166
|
-
|
|
167
|
-
|
|
216
|
+
# Ensure KEV due date is not in the past
|
|
217
|
+
kev_due_validated = self._ensure_future_due_date(
|
|
218
|
+
kev_due_date, 30
|
|
219
|
+
) # Use 30 days as fallback for KEV
|
|
220
|
+
logger.debug(f"Using KEV due date {kev_due_validated} for CVE {cve}")
|
|
221
|
+
return kev_due_validated
|
|
168
222
|
else:
|
|
169
223
|
# KEV date has passed, calculate new due date from creation
|
|
170
224
|
days_diff = (created_dt - kev_date).days
|
|
171
225
|
# Give at least 30 days from creation for critical KEV items
|
|
172
226
|
adjusted_days = max(30, days_diff)
|
|
173
|
-
|
|
227
|
+
calculated_due_date_obj = get_day_increment(start=created_date, days=adjusted_days)
|
|
228
|
+
calculated_due_date = datetime.combine(calculated_due_date_obj, datetime.min.time()).strftime(
|
|
229
|
+
DATETIME_FORMAT
|
|
230
|
+
)
|
|
231
|
+
# Ensure the adjusted due date is not in the past
|
|
232
|
+
due_date = self._ensure_future_due_date(calculated_due_date, adjusted_days)
|
|
174
233
|
logger.debug(f"KEV date passed, using adjusted due date {due_date} for CVE {cve}")
|
|
175
234
|
return due_date
|
|
176
235
|
|
|
@@ -179,12 +238,64 @@ class DueDateHandler:
|
|
|
179
238
|
|
|
180
239
|
# Fall back to severity-based timeline from integration config
|
|
181
240
|
days = self.integration_timelines.get(severity, self.default_timelines[severity])
|
|
182
|
-
|
|
241
|
+
calculated_due_date_obj = get_day_increment(start=created_date, days=days)
|
|
242
|
+
calculated_due_date = datetime.combine(calculated_due_date_obj, datetime.min.time()).strftime(DATETIME_FORMAT)
|
|
243
|
+
|
|
244
|
+
# Ensure due date is never in the past (allow yesterday for timezone differences)
|
|
245
|
+
due_date = self._ensure_future_due_date(calculated_due_date, days)
|
|
183
246
|
|
|
184
247
|
logger.debug(f"Using {self.integration_name} timeline: {severity.name} = {days} days, due date = {due_date}")
|
|
185
248
|
|
|
186
249
|
return due_date
|
|
187
250
|
|
|
251
|
+
def _ensure_future_due_date(self, calculated_due_date: str, original_days: int) -> str:
|
|
252
|
+
"""
|
|
253
|
+
Ensure the due date is not in the past. If it is, set it to today + original timeline days.
|
|
254
|
+
Behavior is controlled by the noPastDueDates configuration setting.
|
|
255
|
+
|
|
256
|
+
:param str calculated_due_date: The originally calculated due date
|
|
257
|
+
:param int original_days: The original number of days for this severity
|
|
258
|
+
:return: A due date that respects the noPastDueDates setting
|
|
259
|
+
:rtype: str
|
|
260
|
+
"""
|
|
261
|
+
# If noPastDueDates is disabled, return the original date without validation
|
|
262
|
+
if not self.no_past_due_dates:
|
|
263
|
+
logger.debug(
|
|
264
|
+
f"noPastDueDates=False for {self.integration_name}, returning original due date: {calculated_due_date}"
|
|
265
|
+
)
|
|
266
|
+
return calculated_due_date
|
|
267
|
+
|
|
268
|
+
from dateutil.parser import parse as date_parse
|
|
269
|
+
from datetime import date
|
|
270
|
+
|
|
271
|
+
try:
|
|
272
|
+
calculated_date = date_parse(calculated_due_date).date()
|
|
273
|
+
today = date.today()
|
|
274
|
+
|
|
275
|
+
if calculated_date >= today:
|
|
276
|
+
return calculated_due_date
|
|
277
|
+
else:
|
|
278
|
+
# Due date is in the past, calculate new due date from today
|
|
279
|
+
# Use minimum 1 day to ensure it's always in the future
|
|
280
|
+
safe_days = max(1, original_days)
|
|
281
|
+
new_due_date_obj = get_day_increment(start=today, days=safe_days)
|
|
282
|
+
new_due_date = datetime.combine(new_due_date_obj, datetime.min.time()).strftime(DATETIME_FORMAT)
|
|
283
|
+
logger.debug(
|
|
284
|
+
f"Due date {calculated_due_date} was in the past for {self.integration_name}. "
|
|
285
|
+
f"Adjusted to {new_due_date} ({safe_days} days from today)."
|
|
286
|
+
)
|
|
287
|
+
return new_due_date
|
|
288
|
+
|
|
289
|
+
except Exception as e:
|
|
290
|
+
logger.warning(f"Failed to validate due date {calculated_due_date}: {e}")
|
|
291
|
+
# If we can't parse the date, return a safe fallback only if noPastDueDates is enabled
|
|
292
|
+
if self.no_past_due_dates:
|
|
293
|
+
safe_days = max(1, original_days)
|
|
294
|
+
fallback_due_date_obj = get_day_increment(start=date.today(), days=safe_days)
|
|
295
|
+
return datetime.combine(fallback_due_date_obj, datetime.min.time()).strftime(DATETIME_FORMAT)
|
|
296
|
+
else:
|
|
297
|
+
return calculated_due_date
|
|
298
|
+
|
|
188
299
|
def get_integration_config(self) -> Dict[str, Any]:
|
|
189
300
|
"""
|
|
190
301
|
Get the full integration configuration from init.yaml
|
|
@@ -205,6 +316,7 @@ class DueDateHandler:
|
|
|
205
316
|
return {
|
|
206
317
|
"integration_name": self.integration_name,
|
|
207
318
|
"use_kev": self._should_use_kev(),
|
|
319
|
+
"no_past_due_dates": self.no_past_due_dates,
|
|
208
320
|
"timelines": {severity.name: days for severity, days in self.integration_timelines.items()},
|
|
209
321
|
"config_source": "init.yaml",
|
|
210
322
|
}
|
|
@@ -48,6 +48,31 @@ def get_thread_workers_max() -> int:
|
|
|
48
48
|
return ScannerVariables.threadMaxWorkers
|
|
49
49
|
|
|
50
50
|
|
|
51
|
+
def _create_config_override(
|
|
52
|
+
config: Optional[Dict[str, Dict]],
|
|
53
|
+
integration_name: str,
|
|
54
|
+
critical: Optional[int],
|
|
55
|
+
high: Optional[int],
|
|
56
|
+
moderate: Optional[int],
|
|
57
|
+
low: Optional[int],
|
|
58
|
+
) -> Dict[str, Dict]:
|
|
59
|
+
"""Create a config override for legacy parameter support."""
|
|
60
|
+
override_config = config.copy() if config else {}
|
|
61
|
+
if "issues" not in override_config:
|
|
62
|
+
override_config["issues"] = {}
|
|
63
|
+
if integration_name not in override_config["issues"]:
|
|
64
|
+
override_config["issues"][integration_name] = {}
|
|
65
|
+
|
|
66
|
+
integration_config = override_config["issues"][integration_name]
|
|
67
|
+
severity_params = {"critical": critical, "high": high, "moderate": moderate, "low": low}
|
|
68
|
+
|
|
69
|
+
for param_name, param_value in severity_params.items():
|
|
70
|
+
if param_value is not None:
|
|
71
|
+
integration_config[param_name] = param_value
|
|
72
|
+
|
|
73
|
+
return override_config
|
|
74
|
+
|
|
75
|
+
|
|
51
76
|
def issue_due_date(
|
|
52
77
|
severity: regscale_models.IssueSeverity,
|
|
53
78
|
created_date: str,
|
|
@@ -61,6 +86,9 @@ def issue_due_date(
|
|
|
61
86
|
"""
|
|
62
87
|
Calculate the due date for an issue based on its severity and creation date.
|
|
63
88
|
|
|
89
|
+
DEPRECATED: This function is kept for backward compatibility. New code should use DueDateHandler directly.
|
|
90
|
+
This function now uses DueDateHandler internally to ensure consistent behavior and proper validation.
|
|
91
|
+
|
|
64
92
|
:param regscale_models.IssueSeverity severity: The severity of the issue.
|
|
65
93
|
:param str created_date: The creation date of the issue.
|
|
66
94
|
:param Optional[int] critical: Days until due for high severity issues.
|
|
@@ -72,40 +100,19 @@ def issue_due_date(
|
|
|
72
100
|
:return: The due date for the issue.
|
|
73
101
|
:rtype: str
|
|
74
102
|
"""
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
regscale_models.IssueSeverity.Critical: critical,
|
|
89
|
-
regscale_models.IssueSeverity.High: high,
|
|
90
|
-
regscale_models.IssueSeverity.Moderate: moderate,
|
|
91
|
-
regscale_models.IssueSeverity.Low: low,
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if title and config:
|
|
95
|
-
# if title in a config key, use that key
|
|
96
|
-
issues_dict = config.get("issues", {})
|
|
97
|
-
matching_key = next((key.lower() for key in issues_dict if title.lower() in key.lower()), None)
|
|
98
|
-
if matching_key:
|
|
99
|
-
title_config = issues_dict.get(matching_key, {})
|
|
100
|
-
due_date_map = {
|
|
101
|
-
regscale_models.IssueSeverity.Critical: title_config.get("critical", critical),
|
|
102
|
-
regscale_models.IssueSeverity.High: title_config.get("high", high),
|
|
103
|
-
regscale_models.IssueSeverity.Moderate: title_config.get("moderate", moderate),
|
|
104
|
-
regscale_models.IssueSeverity.Low: title_config.get("low", low),
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
days = due_date_map.get(severity, low)
|
|
108
|
-
return date_str(get_day_increment(start=created_date, days=days))
|
|
103
|
+
integration_name = title or "default"
|
|
104
|
+
|
|
105
|
+
# Check if individual parameters need config override
|
|
106
|
+
if any(param is not None for param in [critical, high, moderate, low]):
|
|
107
|
+
config = _create_config_override(config, integration_name, critical, high, moderate, low)
|
|
108
|
+
|
|
109
|
+
due_date_handler = DueDateHandler(integration_name, config=config)
|
|
110
|
+
return due_date_handler.calculate_due_date(
|
|
111
|
+
severity=severity,
|
|
112
|
+
created_date=created_date,
|
|
113
|
+
cve=None, # Legacy function doesn't have CVE parameter
|
|
114
|
+
title=title,
|
|
115
|
+
)
|
|
109
116
|
|
|
110
117
|
|
|
111
118
|
class ManagedDefaultDict(Generic[K, V]):
|
|
@@ -2106,7 +2113,9 @@ class ScannerIntegration(ABC):
|
|
|
2106
2113
|
|
|
2107
2114
|
def _set_issue_due_date(self, issue: regscale_models.Issue, finding: IntegrationFinding) -> None:
|
|
2108
2115
|
"""Set the due date for the issue using DueDateHandler."""
|
|
2116
|
+
# Always calculate or validate due date to ensure it's not in the past
|
|
2109
2117
|
if not finding.due_date:
|
|
2118
|
+
# No due date set, calculate new one
|
|
2110
2119
|
try:
|
|
2111
2120
|
base_created = finding.date_created or issue.dateCreated
|
|
2112
2121
|
finding.due_date = self.due_date_handler.calculate_due_date(
|
|
@@ -2125,6 +2134,12 @@ class ScannerIntegration(ABC):
|
|
|
2125
2134
|
cve=finding.cve,
|
|
2126
2135
|
title=finding.title or self.title,
|
|
2127
2136
|
)
|
|
2137
|
+
else:
|
|
2138
|
+
# Due date already exists, but validate it's not in the past (if noPastDueDates is enabled)
|
|
2139
|
+
finding.due_date = self.due_date_handler._ensure_future_due_date(
|
|
2140
|
+
finding.due_date, self.due_date_handler.integration_timelines.get(finding.severity, 60)
|
|
2141
|
+
)
|
|
2142
|
+
|
|
2128
2143
|
issue.dueDate = finding.due_date
|
|
2129
2144
|
|
|
2130
2145
|
def _set_additional_issue_fields(
|
|
@@ -1,9 +1,54 @@
|
|
|
1
1
|
{
|
|
2
2
|
"title": "CISA Catalog of Known Exploited Vulnerabilities",
|
|
3
|
-
"catalogVersion": "2025.09.
|
|
4
|
-
"dateReleased": "2025-09-
|
|
5
|
-
"count":
|
|
3
|
+
"catalogVersion": "2025.09.25",
|
|
4
|
+
"dateReleased": "2025-09-25T16:17:38.0447Z",
|
|
5
|
+
"count": 1417,
|
|
6
6
|
"vulnerabilities": [
|
|
7
|
+
{
|
|
8
|
+
"cveID": "CVE-2025-20362",
|
|
9
|
+
"vendorProject": "Cisco",
|
|
10
|
+
"product": "Secure Firewall Adaptive Security Appliance and Secure Firewall Threat Defense",
|
|
11
|
+
"vulnerabilityName": "Cisco Secure Firewall Adaptive Security (ASA) Appliance and Secure Firewall Threat Defense (FTD) Missing Authorization Vulnerability",
|
|
12
|
+
"dateAdded": "2025-09-25",
|
|
13
|
+
"shortDescription": "Cisco Secure Firewall Adaptive Security (ASA) Appliance and Secure Firewall Threat Defense (FTD) Software VPN Web Server contain a missing authorization vulnerability. This vulnerability could be chained with CVE-2025-20333.",
|
|
14
|
+
"requiredAction": "The KEV due date refers to the deadline by which FCEB agencies are expected to review and begin implementing the guidance outlined in Emergency Directive (ED) 25-03 (URL listed below in Notes). Agencies must follow the mitigation steps provided by CISA (URL listed below in Notes) and vendor\u2019s instructions (URL listed below in Notes). Adhere to the applicable BOD 22-01 guidance for cloud services or discontinue use of the product if mitigations are not available.",
|
|
15
|
+
"dueDate": "2025-09-26",
|
|
16
|
+
"knownRansomwareCampaignUse": "Unknown",
|
|
17
|
+
"notes": "CISA Mitigation Instructions: https:\/\/www.cisa.gov\/news-events\/directives\/ed-25-03-identify-and-mitigate-potential-compromise-cisco-devices ; https:\/\/www.cisa.gov\/news-events\/directives\/supplemental-direction-ed-25-03-core-dump-and-hunt-instructions ; https:\/\/www.cisa.gov\/eviction-strategies-tool\/create-from-template ; https:\/\/sec.cloudapps.cisco.com\/security\/center\/resources\/asa_ftd_continued_attacks ; https:\/\/sec.cloudapps.cisco.com\/security\/center\/private\/resources\/asa_ftd_continued_attacks#Details ; https:\/\/sec.cloudapps.cisco.com\/security\/center\/content\/CiscoSecurityAdvisory\/cisco-sa-asaftd-webvpn-YROOTUW ; https:\/\/nvd.nist.gov\/vuln\/detail\/CVE-2025-20362",
|
|
18
|
+
"cwes": [
|
|
19
|
+
"CWE-862"
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"cveID": "CVE-2025-20333",
|
|
24
|
+
"vendorProject": "Cisco",
|
|
25
|
+
"product": "Secure Firewall Adaptive Security Appliance and Secure Firewall Threat Defense",
|
|
26
|
+
"vulnerabilityName": "Cisco Secure Firewall Adaptive Security Appliance (ASA) and Secure Firewall Threat Defense (FTD) Buffer Overflow Vulnerability",
|
|
27
|
+
"dateAdded": "2025-09-25",
|
|
28
|
+
"shortDescription": "Cisco Secure Firewall Adaptive Security (ASA) Appliance and Secure Firewall Threat Defense (FTD) Software VPN Web Server contain a buffer overflow vulnerability that allows for remote code execution. This vulnerability could be chained with CVE-2025-20362.",
|
|
29
|
+
"requiredAction": "The KEV due date refers to the deadline by which FCEB agencies are expected to review and begin implementing the guidance outlined in Emergency Directive (ED) 25-03 (URL listed below in Notes). Agencies must follow the mitigation steps provided by CISA (URL listed below in Notes) and vendor\u2019s instructions (URL listed below in Notes). Adhere to the applicable BOD 22-01 guidance for cloud services or discontinue use of the product if mitigations are not available.",
|
|
30
|
+
"dueDate": "2025-09-26",
|
|
31
|
+
"knownRansomwareCampaignUse": "Unknown",
|
|
32
|
+
"notes": "CISA Mitigation Instructions: https:\/\/www.cisa.gov\/news-events\/directives\/ed-25-03-identify-and-mitigate-potential-compromise-cisco-devices ; https:\/\/www.cisa.gov\/news-events\/directives\/supplemental-direction-ed-25-03-core-dump-and-hunt-instructions ; https:\/\/www.cisa.gov\/eviction-strategies-tool\/create-from-template ; https:\/\/sec.cloudapps.cisco.com\/security\/center\/resources\/asa_ftd_continued_attacks ; https:\/\/sec.cloudapps.cisco.com\/security\/center\/private\/resources\/asa_ftd_continued_attacks#Details ; https:\/\/sec.cloudapps.cisco.com\/security\/center\/content\/CiscoSecurityAdvisory\/cisco-sa-asaftd-webvpn-z5xP8EUB ; https:\/\/nvd.nist.gov\/vuln\/detail\/CVE-2025-20333",
|
|
33
|
+
"cwes": [
|
|
34
|
+
"CWE-120"
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"cveID": "CVE-2025-10585",
|
|
39
|
+
"vendorProject": "Google",
|
|
40
|
+
"product": "Chromium V8",
|
|
41
|
+
"vulnerabilityName": "Google Chromium V8 Type Confusion Vulnerability",
|
|
42
|
+
"dateAdded": "2025-09-23",
|
|
43
|
+
"shortDescription": "Google Chromium contains a type confusion vulnerability in the V8 JavaScript and WebAssembly engine.",
|
|
44
|
+
"requiredAction": "Apply mitigations per vendor instructions, follow applicable BOD 22-01 guidance for cloud services, or discontinue use of the product if mitigations are unavailable.",
|
|
45
|
+
"dueDate": "2025-10-14",
|
|
46
|
+
"knownRansomwareCampaignUse": "Unknown",
|
|
47
|
+
"notes": "https:\/\/chromereleases.googleblog.com\/2025\/09\/stable-channel-update-for-desktop_17.html ; https:\/\/nvd.nist.gov\/vuln\/detail\/CVE-2025-10585",
|
|
48
|
+
"cwes": [
|
|
49
|
+
"CWE-843"
|
|
50
|
+
]
|
|
51
|
+
},
|
|
7
52
|
{
|
|
8
53
|
"cveID": "CVE-2025-5086",
|
|
9
54
|
"vendorProject": "Dassault Syst\u00e8mes",
|