@cryptotaxi247 / CoPilot / commits / 779a31dd

Vuln list all (#252)

* feat: Add endpoint to fetch agent vulnerabilities by severity The code changes in this commit add a new endpoint to fetch agent vulnerabilities based on their severity. The `get_agent_vulnerabilities` function in the `agents.py` file now accepts a `vulnerability_severity` parameter, allowing users to specify the severity level of the vulnerabilities they want to retrieve. This enhancement improves the flexibility and granularity of vulnerability fetching in the application. * refactor: Update vulnerability title in process_single_vulnerability function * refactor: Update index prefix in build_index_set_config function * feat: Add EDR_SYSTEM_VULNERABILITIES_NEW dashboard to WazuhDashboard enum * feat: Create vulnerability datasource for Grafana This code change adds a new function, `create_vulnerability_datasource`, to the `grafana.py` file in the `customer_provisioning/services` directory. This function is used to create a Grafana datasource for Wazuh vulnerabilities using the OpenSearch Data Source. It is specifically designed for Wazuh versions 4.8.0 and above. * update: dependencies * update: design token * improve: PinnedPage component * update: indecies marquee speed * update: Vulnerability api * update: Vulnerabilities tab * precommit fixes * feat: Update agent vulnerability fetching logic for Wazuh Manager version 4.8.0 or higher This code change modifies the `get_agent_vulnerabilities` function in the `agents.py` file. It adds a check for the Wazuh Manager version and if it is 4.8.0 or higher, it fetches the vulnerabilities using the new API. This improves the vulnerability fetching process for agents with newer Wazuh Manager versions. --------- Co-authored-by: Davide Di Modica <webmaster.ddm@gmail.com>

taylor_socfortress committed Jun 24, 2024 at 20:01 UTC 779a31dde32086d07ec8511d0bc5337a520f9136
19 files changed +3208 -318
backend/app/agents/routes/agents.py
+10 -4
@@ -4,6 +4,7 @@ import asyncio
4 from fastapi import APIRouter
5 from fastapi import Depends
6 from fastapi import HTTPException
7 +from fastapi import Path
8 from fastapi import Security
9 from loguru import logger
10 from packaging import version
@@ -23,6 +24,7 @@ from app.agents.services.status import get_outdated_agents_wazuh
24 from app.agents.services.sync import sync_agents_velociraptor
25 from app.agents.services.sync import sync_agents_wazuh
26 from app.agents.velociraptor.services.agents import delete_agent_velociraptor
27 +from app.agents.wazuh.schema.agents import VulnSeverity
28 from app.agents.wazuh.schema.agents import WazuhAgentScaPolicyResultsResponse
29 from app.agents.wazuh.schema.agents import WazuhAgentScaResponse
30 from app.agents.wazuh.schema.agents import WazuhAgentVulnerabilitiesResponse
@@ -432,12 +434,15 @@ async def upgrade_wazuh_agent_route(
434
435
436 @agents_router.get(
435 - "/{agent_id}/vulnerabilities",
437 + "/{agent_id}/vulnerabilities/{vulnerability_severity}",
438 response_model=WazuhAgentVulnerabilitiesResponse,
439 description="Get agent vulnerabilities",
440 dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
441 )
440 -async def get_agent_vulnerabilities(agent_id: str) -> WazuhAgentVulnerabilitiesResponse:
442 +async def get_agent_vulnerabilities(
443 + agent_id: str,
444 + vulnerability_severity: VulnSeverity = Path(..., description="The severity of the vulnerabilities to fetch."),
445 +) -> WazuhAgentVulnerabilitiesResponse:
446 """
447 Fetches the vulnerabilities of a specific agent.
448
@@ -450,8 +455,9 @@ async def get_agent_vulnerabilities(agent_id: str) -> WazuhAgentVulnerabilitiesR
455 logger.info(f"Fetching agent {agent_id} vulnerabilities")
456 wazuh_new = await check_wazuh_manager_version()
457 if wazuh_new is True:
453 - return await collect_agent_vulnerabilities_new(agent_id)
454 - return await collect_agent_vulnerabilities(agent_id)
458 + logger.info("Wazuh Manager version is 4.8.0 or higher. Fetching vulnerabilities using new API")
459 + return await collect_agent_vulnerabilities_new(agent_id, vulnerability_severity.value)
460 + return await collect_agent_vulnerabilities(agent_id, vulnerability_severity.value)
461
462
463 @agents_router.get(
backend/app/agents/wazuh/schema/agents.py
+8
@@ -1,4 +1,5 @@
1 from datetime import datetime
2 +from enum import Enum
3 from typing import List
4 from typing import Optional
5
@@ -6,6 +7,13 @@ from pydantic import BaseModel
7 from pydantic import Field
8
9
10 +class VulnSeverity(Enum):
11 + Low = "Low"
12 + Medium = "Medium"
13 + High = "High"
14 + Critical = "Critical"
15 +
16 +
17 class WazuhAgent(BaseModel):
18 agent_id: str = Field(..., alias="agent_id")
19 agent_name: str = Field(..., alias="hostname")
backend/app/agents/wazuh/services/vulnerabilities.py
+20 -10
@@ -10,7 +10,7 @@ from app.connectors.wazuh_indexer.utils.universal import create_wazuh_indexer_cl
10 from app.connectors.wazuh_manager.utils.universal import send_get_request
11
12
13 -async def collect_agent_vulnerabilities(agent_id: str):
13 +async def collect_agent_vulnerabilities(agent_id: str, vulnerability_severity: str):
14 """
15 Collect agent vulnerabilities from Wazuh Manager.
16 Used when Wazuh Manager is below 4.8.0
@@ -27,9 +27,9 @@ async def collect_agent_vulnerabilities(agent_id: str):
27 logger.info(f"Collecting agent {agent_id} vulnerabilities from Wazuh Manager")
28 agent_vulnerabilities = await send_get_request(
29 endpoint=f"/vulnerability/{agent_id}",
30 + params={"severity": vulnerability_severity},
31 )
32 if agent_vulnerabilities["success"] is False:
32 - return None
33 raise HTTPException(status_code=500, detail=agent_vulnerabilities["message"])
34
35 processed_vulnerabilities = process_agent_vulnerabilities(
@@ -70,7 +70,7 @@ def process_agent_vulnerabilities(
70 )
71
72
73 -async def collect_agent_vulnerabilities_new(agent_id: str):
73 +async def collect_agent_vulnerabilities_new(agent_id: str, vulnerability_severity: str):
74 """
75 Collects vulnerabilities for a specific agent from the Wazuh Indexer Index.
76 Used when Wazuh-Manager is 4.8.0 or above.
@@ -89,7 +89,7 @@ async def collect_agent_vulnerabilities_new(agent_id: str):
89
90 vulnerabilities_indices = filter_vulnerabilities_indices(indices.indices_list)
91
92 - agent_vulnerabilities = await collect_vulnerabilities(es, vulnerabilities_indices, agent_id)
92 + agent_vulnerabilities = await collect_vulnerabilities(es, vulnerabilities_indices, agent_id, vulnerability_severity)
93
94 processed_vulnerabilities = process_agent_vulnerabilities_new(agent_vulnerabilities)
95
@@ -104,15 +104,25 @@ def filter_vulnerabilities_indices(indices_list):
104 return [index for index in indices_list if index.startswith("wazuh-states-vulnerabilities")]
105
106
107 -async def collect_vulnerabilities(es, vulnerabilities_indices, agent_id):
107 +async def collect_vulnerabilities(es, vulnerabilities_indices, agent_id, vulnerability_severity="Critical"):
108 agent_vulnerabilities = []
109 for index in vulnerabilities_indices:
110 - query = {"query": {"match": {"agent.id": agent_id}}}
111 - response = es.search(index=index, body=query)
110 + query = {
111 + "query": {"bool": {"must": [{"match": {"agent.id": agent_id}}, {"match": {"vulnerability.severity": vulnerability_severity}}]}},
112 + }
113 + page = es.search(index=index, body=query, scroll="2m")
114 + sid = page["_scroll_id"]
115 + scroll_size = len(page["hits"]["hits"])
116 +
117 + while scroll_size > 0:
118 + for hit in page["hits"]["hits"]:
119 + vulnerability = hit["_source"]
120 + agent_vulnerabilities.append(vulnerability)
121 +
122 + page = es.scroll(scroll_id=sid, scroll="2m")
123 + sid = page["_scroll_id"]
124 + scroll_size = len(page["hits"]["hits"])
125
113 - for hit in response["hits"]["hits"]:
114 - vulnerability = hit["_source"]
115 - agent_vulnerabilities.append(vulnerability)
126 return agent_vulnerabilities
127
128
backend/app/connectors/grafana/dashboards/Wazuh/edr_system_vulnerabilities_new.json new
+2663
@@ -0,0 +1,2663 @@
1 +{
2 + "annotations": {
3 + "list": [
4 + {
5 + "builtIn": 1,
6 + "datasource": {
7 + "type": "datasource",
8 + "uid": "grafana"
9 + },
10 + "enable": true,
11 + "hide": true,
12 + "iconColor": "rgba(0, 211, 255, 1)",
13 + "name": "Annotations & Alerts",
14 + "target": {
15 + "limit": 100,
16 + "matchAny": false,
17 + "tags": [],
18 + "type": "dashboard"
19 + },
20 + "type": "dashboard"
21 + }
22 + ]
23 + },
24 + "editable": false,
25 + "fiscalYearStartMonth": 0,
26 + "graphTooltip": 0,
27 + "id": null,
28 + "links": [
29 + {
30 + "asDropdown": true,
31 + "icon": "external link",
32 + "includeVars": true,
33 + "keepTime": true,
34 + "tags": ["EDR"],
35 + "targetBlank": true,
36 + "title": "",
37 + "type": "dashboards"
38 + }
39 + ],
40 + "liveNow": false,
41 + "panels": [
42 + {
43 + "datasource": {
44 + "type": "grafana-opensearch-datasource",
45 + "uid": "replace_datasource_uid"
46 + },
47 + "gridPos": {
48 + "h": 1,
49 + "w": 24,
50 + "x": 0,
51 + "y": 0
52 + },
53 + "id": 58,
54 + "title": "SYSTEM OS AND SOFTWARE VULNERABILITIES - SUMMARY",
55 + "type": "row"
56 + },
57 + {
58 + "datasource": {
59 + "type": "grafana-opensearch-datasource",
60 + "uid": "replace_datasource_uid"
61 + },
62 + "fieldConfig": {
63 + "defaults": {
64 + "mappings": [
65 + {
66 + "options": {
67 + "match": "null",
68 + "result": {
69 + "text": "N/A"
70 + }
71 + },
72 + "type": "special"
73 + }
74 + ],
75 + "thresholds": {
76 + "mode": "absolute",
77 + "steps": [
78 + {
79 + "color": "dark-orange",
80 + "value": null
81 + }
82 + ]
83 + },
84 + "unit": "short"
85 + },
86 + "overrides": []
87 + },
88 + "gridPos": {
89 + "h": 7,
90 + "w": 4,
91 + "x": 0,
92 + "y": 1
93 + },
94 + "id": 43,
95 + "links": [],
96 + "options": {
97 + "colorMode": "value",
98 + "graphMode": "area",
99 + "justifyMode": "auto",
100 + "orientation": "horizontal",
101 + "reduceOptions": {
102 + "calcs": ["sum"],
103 + "fields": "",
104 + "values": false
105 + },
106 + "text": {},
107 + "textMode": "auto",
108 + "wideLayout": true
109 + },
110 + "pluginVersion": "10.2.3",
111 + "targets": [
112 + {
113 + "bucketAggs": [
114 + {
115 + "field": "timestamp",
116 + "id": "2",
117 + "settings": {
118 + "interval": "auto",
119 + "min_doc_count": 0,
120 + "trimEdges": 0
121 + },
122 + "type": "date_histogram"
123 + }
124 + ],
125 + "datasource": {
126 + "type": "grafana-opensearch-datasource",
127 + "uid": "replace_datasource_uid"
128 + },
129 + "metrics": [
130 + {
131 + "field": "select field",
132 + "id": "1",
133 + "type": "count"
134 + }
135 + ],
136 + "query": "agent.name:$agent_name",
137 + "refId": "A",
138 + "timeField": "vulnerability.detected_at"
139 + }
140 + ],
141 + "title": "VULNERABILITY EVENTS",
142 + "type": "stat"
143 + },
144 + {
145 + "columns": [],
146 + "datasource": {
147 + "type": "grafana-opensearch-datasource",
148 + "uid": "replace_datasource_uid"
149 + },
150 + "fontSize": "100%",
151 + "gridPos": {
152 + "h": 7,
153 + "w": 8,
154 + "x": 4,
155 + "y": 1
156 + },
157 + "id": 31,
158 + "showHeader": true,
159 + "sort": {
160 + "col": 0,
161 + "desc": true
162 + },
163 + "styles": [
164 + {
165 + "alias": "Time",
166 + "align": "auto",
167 + "dateFormat": "YYYY-MM-DD HH:mm:ss",
168 + "pattern": "Time",
169 + "type": "date"
170 + },
171 + {
172 + "alias": "",
173 + "align": "auto",
174 + "colorMode": "row",
175 + "colors": ["rgba(50, 172, 45, 0.97)", "rgba(237, 129, 40, 0.89)", "#FA6400"],
176 + "dateFormat": "YYYY-MM-DD HH:mm:ss",
177 + "decimals": -1,
178 + "mappingType": 1,
179 + "pattern": "Count",
180 + "thresholds": ["0", "1"],
181 + "type": "number",
182 + "unit": "short"
183 + },
184 + {
185 + "alias": "AGENT",
186 + "align": "auto",
187 + "colors": ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
188 + "dateFormat": "YYYY-MM-DD HH:mm:ss",
189 + "decimals": 2,
190 + "mappingType": 1,
191 + "pattern": "agent_name",
192 + "thresholds": [],
193 + "type": "number",
194 + "unit": "short"
195 + }
196 + ],
197 + "targets": [
198 + {
199 + "bucketAggs": [
200 + {
201 + "fake": true,
202 + "field": "agent.name",
203 + "id": "4",
204 + "settings": {
205 + "min_doc_count": 1,
206 + "order": "desc",
207 + "orderBy": "_term",
208 + "size": "0"
209 + },
210 + "type": "terms"
211 + }
212 + ],
213 + "datasource": {
214 + "type": "grafana-opensearch-datasource",
215 + "uid": "replace_datasource_uid"
216 + },
217 + "metrics": [
218 + {
219 + "field": "select field",
220 + "id": "1",
221 + "type": "count"
222 + }
223 + ],
224 + "query": "agent.name:$agent_name",
225 + "refId": "A",
226 + "timeField": "timestamp"
227 + }
228 + ],
229 + "title": "AGENTS",
230 + "transform": "table",
231 + "type": "table-old"
232 + },
233 + {
234 + "columns": [],
235 + "datasource": {
236 + "type": "grafana-opensearch-datasource",
237 + "uid": "replace_datasource_uid"
238 + },
239 + "fontSize": "100%",
240 + "gridPos": {
241 + "h": 7,
242 + "w": 6,
243 + "x": 12,
244 + "y": 1
245 + },
246 + "id": 54,
247 + "showHeader": true,
248 + "sort": {
249 + "col": 0,
250 + "desc": true
251 + },
252 + "styles": [
253 + {
254 + "alias": "Time",
255 + "align": "auto",
256 + "dateFormat": "YYYY-MM-DD HH:mm:ss",
257 + "pattern": "Time",
258 + "type": "date"
259 + },
260 + {
261 + "alias": "",
262 + "align": "auto",
263 + "colorMode": "row",
264 + "colors": ["rgba(50, 172, 45, 0.97)", "rgba(237, 129, 40, 0.89)", "#FA6400"],
265 + "dateFormat": "YYYY-MM-DD HH:mm:ss",
266 + "decimals": -1,
267 + "mappingType": 1,
268 + "pattern": "Count",
269 + "thresholds": ["0", "1"],
270 + "type": "number",
271 + "unit": "short"
272 + },
273 + {
274 + "alias": "CVSS2",
275 + "align": "auto",
276 + "colors": ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
277 + "dateFormat": "YYYY-MM-DD HH:mm:ss",
278 + "decimals": 2,
279 + "mappingType": 1,
280 + "pattern": "data_vulnerability_cvss_cvss2_base_score",
281 + "thresholds": [],
282 + "type": "number",
283 + "unit": "short"
284 + }
285 + ],
286 + "targets": [
287 + {
288 + "bucketAggs": [
289 + {
290 + "fake": true,
291 + "field": "vulnerability.score.base",
292 + "id": "4",
293 + "settings": {
294 + "min_doc_count": 1,
295 + "order": "desc",
296 + "orderBy": "_term",
297 + "size": "0"
298 + },
299 + "type": "terms"
300 + }
301 + ],
302 + "datasource": {
303 + "type": "grafana-opensearch-datasource",
304 + "uid": "replace_datasource_uid"
305 + },
306 + "metrics": [
307 + {
308 + "field": "select field",
309 + "id": "1",
310 + "type": "count"
311 + }
312 + ],
313 + "query": "agent.name:$agent_name",
314 + "refId": "A",
315 + "timeField": "timestamp"
316 + }
317 + ],
318 + "title": "CVSS2 BASE SCORE",
319 + "transform": "table",
320 + "type": "table-old"
321 + },
322 + {
323 + "datasource": {
324 + "type": "grafana-opensearch-datasource",
325 + "uid": "replace_datasource_uid"
326 + },
327 + "fieldConfig": {
328 + "defaults": {
329 + "color": {
330 + "mode": "thresholds"
331 + },
332 + "custom": {
333 + "align": "auto",
334 + "cellOptions": {
335 + "type": "auto"
336 + },
337 + "filterable": false,
338 + "inspect": false
339 + },
340 + "mappings": [],
341 + "thresholds": {
342 + "mode": "absolute",
343 + "steps": [
344 + {
345 + "color": "green",
346 + "value": null
347 + },
348 + {
349 + "color": "red",
350 + "value": 80
351 + }
352 + ]
353 + }
354 + },
355 + "overrides": [
356 + {
357 + "matcher": {
358 + "id": "byName",
359 + "options": "Time"
360 + },
361 + "properties": [
362 + {
363 + "id": "displayName",
364 + "value": "Time"
365 + },
366 + {
367 + "id": "unit",
368 + "value": "time: YYYY-MM-DD HH:mm:ss"
369 + },
370 + {
371 + "id": "custom.align"
372 + }
373 + ]
374 + },
375 + {
376 + "matcher": {
377 + "id": "byName",
378 + "options": ""
379 + },
380 + "properties": [
381 + {
382 + "id": "unit",
383 + "value": "short"
384 + },
385 + {
386 + "id": "decimals",
387 + "value": 2
388 + },
389 + {
390 + "id": "custom.align"
391 + }
392 + ]
393 + },
394 + {
395 + "matcher": {
396 + "id": "byName",
397 + "options": "data_vulnerability_cve"
398 + },
399 + "properties": [
400 + {
401 + "id": "displayName",
402 + "value": "CVE"
403 + },
404 + {
405 + "id": "unit",
406 + "value": "short"
407 + },
408 + {
409 + "id": "decimals",
410 + "value": -1
411 + },
412 + {
413 + "id": "links",
414 + "value": [
415 + {
416 + "targetBlank": true,
417 + "title": "NVD - NIST DATABASE",
418 + "url": "https://nvd.nist.gov/vuln/detail/${__value.text}"
419 + }
420 + ]
421 + },
422 + {
423 + "id": "custom.align",
424 + "value": "left"
425 + }
426 + ]
427 + },
428 + {
429 + "matcher": {
430 + "id": "byName",
431 + "options": "Unique Count"
432 + },
433 + "properties": [
434 + {
435 + "id": "displayName",
436 + "value": "HITS"
437 + },
438 + {
439 + "id": "unit",
440 + "value": "short"
441 + },
442 + {
443 + "id": "decimals",
444 + "value": -1
445 + },
446 + {
447 + "id": "custom.align"
448 + }
449 + ]
450 + },
451 + {
452 + "matcher": {
453 + "id": "byName",
454 + "options": "EPSS"
455 + },
456 + "properties": [
457 + {
458 + "id": "links",
459 + "value": [
460 + {
461 + "targetBlank": true,
462 + "title": "Exploit Prediction Scoring System",
463 + "url": "https://www.first.org/epss/"
464 + }
465 + ]
466 + }
467 + ]
468 + }
469 + ]
470 + },
471 + "gridPos": {
472 + "h": 7,
473 + "w": 6,
474 + "x": 18,
475 + "y": 1
476 + },
477 + "id": 47,
478 + "options": {
479 + "cellHeight": "sm",
480 + "footer": {
481 + "countRows": false,
482 + "fields": "",
483 + "reducer": ["sum"],
484 + "show": false
485 + },
486 + "showHeader": true
487 + },
488 + "pluginVersion": "10.2.3",
489 + "targets": [
490 + {
491 + "bucketAggs": [
492 + {
493 + "fake": true,
494 + "field": "vulnerability.id",
495 + "id": "7",
496 + "settings": {
497 + "min_doc_count": 1,
498 + "order": "desc",
499 + "orderBy": "_term",
500 + "size": "10"
501 + },
502 + "type": "terms"
503 + }
504 + ],
505 + "datasource": {
506 + "type": "grafana-opensearch-datasource",
507 + "uid": "replace_datasource_uid"
508 + },
509 + "metrics": [
510 + {
511 + "field": "vulnerability.id",
512 + "id": "1",
513 + "meta": {},
514 + "settings": {},
515 + "type": "cardinality"
516 + }
517 + ],
518 + "query": "agent.name:$agent_name",
519 + "refId": "A",
520 + "timeField": "vulnerability.detected_at"
521 + }
522 + ],
523 + "title": "CVEs",
524 + "transformations": [
525 + {
526 + "id": "organize",
527 + "options": {
528 + "excludeByName": {},
529 + "includeByName": {},
530 + "indexByName": {},
531 + "renameByName": {
532 + "data_vulnerability_cve": "CVE",
533 + "epss_epss": "EPSS",
534 + "vulnerability.id": "CVE"
535 + }
536 + }
537 + }
538 + ],
539 + "type": "table"
540 + },
541 + {
542 + "datasource": {
543 + "type": "grafana-opensearch-datasource",
544 + "uid": "replace_datasource_uid"
545 + },
546 + "fieldConfig": {
547 + "defaults": {
548 + "mappings": [],
549 + "thresholds": {
550 + "mode": "absolute",
551 + "steps": [
552 + {
553 + "color": "green",
554 + "value": null
555 + },
556 + {
557 + "color": "red",
558 + "value": 80
559 + }
560 + ]
561 + }
562 + },
563 + "overrides": []
564 + },
565 + "gridPos": {
566 + "h": 9,
567 + "w": 17,
568 + "x": 0,
569 + "y": 8
570 + },
571 + "id": 37,
572 + "options": {
573 + "displayMode": "gradient",
574 + "maxVizHeight": 300,
575 + "minVizHeight": 10,
576 + "minVizWidth": 0,
577 + "namePlacement": "auto",
578 + "orientation": "horizontal",
579 + "reduceOptions": {
580 + "calcs": ["sum"],
581 + "fields": "",
582 + "values": false
583 + },
584 + "showUnfilled": true,
585 + "sizing": "auto",
586 + "text": {},
587 + "valueMode": "color"
588 + },
589 + "pluginVersion": "10.2.3",
590 + "targets": [
591 + {
592 + "bucketAggs": [
593 + {
594 + "fake": true,
595 + "field": "package.name",
596 + "id": "6",
597 + "settings": {
598 + "min_doc_count": 1,
599 + "order": "desc",
600 + "orderBy": "_count",
601 + "size": "15"
602 + },
603 + "type": "terms"
604 + },
605 + {
606 + "fake": true,
607 + "field": "timestamp",
608 + "id": "5",
609 + "settings": {
610 + "interval": "auto",
611 + "min_doc_count": 0,
612 + "trimEdges": 0
613 + },
614 + "type": "date_histogram"
615 + }
616 + ],
617 + "datasource": {
618 + "type": "grafana-opensearch-datasource",
619 + "uid": "replace_datasource_uid"
620 + },
621 + "metrics": [
622 + {
623 + "field": "type",
624 + "id": "1",
625 + "meta": {},
626 + "settings": {},
627 + "type": "count"
628 + }
629 + ],
630 + "query": "agent.name:$agent_name",
631 + "refId": "A",
632 + "timeField": "vulnerability.detected_at"
633 + }
634 + ],
635 + "title": "VULNERABLE SOFTWARE / PACKAGE",
636 + "type": "bargauge"
637 + },
638 + {
639 + "datasource": {
640 + "type": "grafana-opensearch-datasource",
641 + "uid": "replace_datasource_uid"
642 + },
643 + "fieldConfig": {
644 + "defaults": {
645 + "color": {
646 + "mode": "palette-classic"
647 + },
648 + "custom": {
649 + "hideFrom": {
650 + "legend": false,
651 + "tooltip": false,
652 + "viz": false
653 + }
654 + },
655 + "decimals": 0,
656 + "mappings": [],
657 + "unit": "short"
658 + },
659 + "overrides": [
660 + {
661 + "matcher": {
662 + "id": "byName",
663 + "options": "Critical"
664 + },
665 + "properties": [
666 + {
667 + "id": "color",
668 + "value": {
669 + "fixedColor": "#C4162A",
670 + "mode": "fixed"
671 + }
672 + }
673 + ]
674 + },
675 + {
676 + "matcher": {
677 + "id": "byName",
678 + "options": "High"
679 + },
680 + "properties": [
681 + {
682 + "id": "color",
683 + "value": {
684 + "fixedColor": "#F2495C",
685 + "mode": "fixed"
686 + }
687 + }
688 + ]
689 + },
690 + {
691 + "matcher": {
692 + "id": "byName",
693 + "options": "Low"
694 + },
695 + "properties": [
696 + {
697 + "id": "color",
698 + "value": {
699 + "fixedColor": "#5794F2",
700 + "mode": "fixed"
701 + }
702 + }
703 + ]
704 + },
705 + {
706 + "matcher": {
707 + "id": "byName",
708 + "options": "Medium"
709 + },
710 + "properties": [
711 + {
712 + "id": "color",
713 + "value": {
714 + "fixedColor": "#FF9830",
715 + "mode": "fixed"
716 + }
717 + }
718 + ]
719 + }
720 + ]
721 + },
722 + "gridPos": {
723 + "h": 9,
724 + "w": 7,
725 + "x": 17,
726 + "y": 8
727 + },
728 + "id": 45,
729 + "links": [],
730 + "maxDataPoints": 3,
731 + "options": {
732 + "legend": {
733 + "calcs": [],
734 + "displayMode": "table",
735 + "placement": "right",
736 + "showLegend": true,
737 + "values": ["value"]
738 + },
739 + "pieType": "donut",
740 + "reduceOptions": {
741 + "calcs": ["sum"],
742 + "fields": "",
743 + "values": false
744 + },
745 + "tooltip": {
746 + "mode": "single",
747 + "sort": "none"
748 + }
749 + },
750 + "targets": [
751 + {
752 + "bucketAggs": [
753 + {
754 + "fake": true,
755 + "field": "vulnerability.severity",
756 + "id": "3",
757 + "settings": {
758 + "min_doc_count": 1,
759 + "order": "desc",
760 + "orderBy": "_count",
761 + "size": "0"
762 + },
763 + "type": "terms"
764 + },
765 + {
766 + "field": "timestamp",
767 + "id": "2",
768 + "settings": {
769 + "interval": "auto",
770 + "min_doc_count": 0,
771 + "trimEdges": 0
772 + },
773 + "type": "date_histogram"
774 + }
775 + ],
776 + "datasource": {
777 + "type": "grafana-opensearch-datasource",
778 + "uid": "replace_datasource_uid"
779 + },
780 + "metrics": [
781 + {
782 + "field": "select field",
783 + "id": "1",
784 + "type": "count"
785 + }
786 + ],
787 + "query": "agent.name:$agent_name",
788 + "refId": "A",
789 + "timeField": "vulnerability.detected_at"
790 + }
791 + ],
792 + "title": "VULNERABILITY LEVELS",
793 + "type": "piechart"
794 + },
795 + {
796 + "collapsed": false,
797 + "datasource": {
798 + "type": "grafana-opensearch-datasource",
799 + "uid": "replace_datasource_uid"
800 + },
801 + "gridPos": {
802 + "h": 1,
803 + "w": 24,
804 + "x": 0,
805 + "y": 17
806 + },
807 + "id": 60,
808 + "panels": [],
809 + "title": "SYSTEM OS AND SOFTWARE VULNERABILITIES - ENTRIES",
810 + "type": "row"
811 + },
812 + {
813 + "datasource": {
814 + "type": "grafana-opensearch-datasource",
815 + "uid": "replace_datasource_uid"
816 + },
817 + "fieldConfig": {
818 + "defaults": {
819 + "color": {
820 + "mode": "thresholds"
821 + },
822 + "custom": {
823 + "align": "auto",
824 + "cellOptions": {
825 + "type": "auto"
826 + },
827 + "filterable": false,
828 + "inspect": false
829 + },
830 + "mappings": [],
831 + "thresholds": {
832 + "mode": "absolute",
833 + "steps": [
834 + {
835 + "color": "green",
836 + "value": null
837 + },
838 + {
839 + "color": "red",
840 + "value": 80
841 + }
842 + ]
843 + }
844 + },
845 + "overrides": [
846 + {
847 + "matcher": {
848 + "id": "byName",
849 + "options": "Time"
850 + },
851 + "properties": [
852 + {
853 + "id": "displayName",
854 + "value": "Time"
855 + },
856 + {
857 + "id": "unit",
858 + "value": "time: YYYY-MM-DD HH:mm:ss"
859 + },
860 + {
861 + "id": "custom.align"
862 + }
863 + ]
864 + },
865 + {
866 + "matcher": {
867 + "id": "byName",
868 + "options": ""
869 + },
870 + "properties": [
871 + {
872 + "id": "unit",
873 + "value": "short"
874 + },
875 + {
876 + "id": "decimals",
877 + "value": 2
878 + },
879 + {
880 + "id": "custom.align"
881 + }
882 + ]
883 + },
884 + {
885 + "matcher": {
886 + "id": "byName",
887 + "options": "data_vulnerability_package_name"
888 + },
889 + "properties": [
890 + {
891 + "id": "displayName",
892 + "value": "PACKAGE NAME"
893 + },
894 + {
895 + "id": "unit",
896 + "value": "short"
897 + },
898 + {
899 + "id": "decimals",
900 + "value": -1
901 + },
902 + {
903 + "id": "custom.align",
904 + "value": "left"
905 + }
906 + ]
907 + },
908 + {
909 + "matcher": {
910 + "id": "byName",
911 + "options": "Unique Count"
912 + },
913 + "properties": [
914 + {
915 + "id": "displayName",
916 + "value": "HITS"
917 + },
918 + {
919 + "id": "unit",
920 + "value": "short"
921 + },
922 + {
923 + "id": "decimals",
924 + "value": -1
925 + },
926 + {
927 + "id": "custom.align"
928 + }
929 + ]
930 + },
931 + {
932 + "matcher": {
933 + "id": "byName",
934 + "options": "data_vulnerability_package_condition"
935 + },
936 + "properties": [
937 + {
938 + "id": "displayName",
939 + "value": "CONDITION"
940 + },
941 + {
942 + "id": "unit",
943 + "value": "short"
944 + },
945 + {
946 + "id": "decimals",
947 + "value": 2
948 + },
949 + {
950 + "id": "custom.align"
951 + }
952 + ]
953 + },
954 + {
955 + "matcher": {
956 + "id": "byName",
957 + "options": "CONDITION"
958 + },
959 + "properties": [
960 + {
961 + "id": "custom.width",
962 + "value": 378
963 + }
964 + ]
965 + }
966 + ]
967 + },
968 + "gridPos": {
969 + "h": 11,
970 + "w": 9,
971 + "x": 0,
972 + "y": 18
973 + },
974 + "id": 53,
975 + "options": {
976 + "cellHeight": "sm",
977 + "footer": {
978 + "countRows": false,
979 + "fields": "",
980 + "reducer": ["sum"],
981 + "show": false
982 + },
983 + "showHeader": true,
984 + "sortBy": []
985 + },
986 + "pluginVersion": "10.2.3",
987 + "targets": [
988 + {
989 + "bucketAggs": [
990 + {
991 + "fake": true,
992 + "field": "package.name",
993 + "id": "8",
994 + "settings": {
995 + "min_doc_count": 1,
996 + "order": "desc",
997 + "orderBy": "_term",
998 + "size": "10"
999 + },
1000 + "type": "terms"
1001 + }
1002 + ],
1003 + "datasource": {
1004 + "type": "grafana-opensearch-datasource",
1005 + "uid": "replace_datasource_uid"
1006 + },
1007 + "metrics": [
1008 + {
1009 + "field": "package.description",
1010 + "id": "1",
1011 + "meta": {},
1012 + "settings": {},
1013 + "type": "cardinality"
1014 + }
1015 + ],
1016 + "query": "agent.name:$agent_name",
1017 + "refId": "A",
1018 + "timeField": "vulnerability.detected_at"
1019 + }
1020 + ],
1021 + "title": "SOFTWARE / PACKAGE",
1022 + "transformations": [
1023 + {
1024 + "id": "merge",
1025 + "options": {
1026 + "reducers": []
1027 + }
1028 + }
1029 + ],
1030 + "type": "table"
1031 + },
1032 + {
1033 + "datasource": {
1034 + "type": "grafana-opensearch-datasource",
1035 + "uid": "replace_datasource_uid"
1036 + },
1037 + "fieldConfig": {
1038 + "defaults": {
1039 + "color": {
1040 + "mode": "palette-classic"
1041 + },
1042 + "custom": {
1043 + "axisBorderShow": false,
1044 + "axisCenteredZero": false,
1045 + "axisColorMode": "text",
1046 + "axisLabel": "",
1047 + "axisPlacement": "auto",
1048 + "barAlignment": 0,
1049 + "drawStyle": "line",
1050 + "fillOpacity": 0,
1051 + "gradientMode": "none",
1052 + "hideFrom": {
1053 + "legend": false,
1054 + "tooltip": false,
1055 + "viz": false
1056 + },
1057 + "insertNulls": false,
1058 + "lineInterpolation": "linear",
1059 + "lineWidth": 1,
1060 + "pointSize": 5,
1061 + "scaleDistribution": {
1062 + "type": "linear"
1063 + },
1064 + "showPoints": "auto",
1065 + "spanNulls": false,
1066 + "stacking": {
1067 + "group": "A",
1068 + "mode": "none"
1069 + },
1070 + "thresholdsStyle": {
1071 + "mode": "off"
1072 + }
1073 + },
1074 + "mappings": [],
1075 + "thresholds": {
1076 + "mode": "absolute",
1077 + "steps": [
1078 + {
1079 + "color": "green",
1080 + "value": null
1081 + },
1082 + {
1083 + "color": "red",
1084 + "value": 80
1085 + }
1086 + ]
1087 + }
1088 + },
1089 + "overrides": []
1090 + },
1091 + "gridPos": {
1092 + "h": 11,
1093 + "w": 15,
1094 + "x": 9,
1095 + "y": 18
1096 + },
1097 + "id": 72,
1098 + "options": {
1099 + "legend": {
1100 + "calcs": [],
1101 + "displayMode": "table",
1102 + "placement": "right",
1103 + "showLegend": true
1104 + },
1105 + "tooltip": {
1106 + "mode": "single",
1107 + "sort": "none"
1108 + }
1109 + },
1110 + "targets": [
1111 + {
1112 + "alias": "",
1113 + "bucketAggs": [
1114 + {
1115 + "field": "agent.name",
1116 + "id": "3",
1117 + "settings": {
1118 + "min_doc_count": "1",
1119 + "order": "desc",
1120 + "orderBy": "_term",
1121 + "size": "10"
1122 + },
1123 + "type": "terms"
1124 + },
1125 + {
1126 + "field": "timestamp",
1127 + "id": "2",
1128 + "settings": {
1129 + "interval": "auto"
1130 + },
1131 + "type": "date_histogram"
1132 + }
1133 + ],
1134 + "datasource": {
1135 + "type": "grafana-opensearch-datasource",
1136 + "uid": "replace_datasource_uid"
1137 + },
1138 + "metrics": [
1139 + {
1140 + "id": "1",
1141 + "type": "count"
1142 + }
1143 + ],
1144 + "query": "agent.name:$agent_name",
1145 + "refId": "A",
1146 + "timeField": "timestamp"
1147 + }
1148 + ],
1149 + "title": "VULNERABILITY EVENTS - HISTOGRAM",
1150 + "type": "timeseries"
1151 + },
1152 + {
1153 + "datasource": {
1154 + "type": "grafana-opensearch-datasource",
1155 + "uid": "replace_datasource_uid"
1156 + },
1157 + "fieldConfig": {
1158 + "defaults": {
1159 + "color": {
1160 + "mode": "thresholds"
1161 + },
1162 + "custom": {
1163 + "align": "auto",
1164 + "cellOptions": {
1165 + "type": "auto"
1166 + },
1167 + "inspect": false
1168 + },
1169 + "mappings": [],
1170 + "thresholds": {
1171 + "mode": "absolute",
1172 + "steps": [
1173 + {
1174 + "color": "green"
1175 + },
1176 + {
1177 + "color": "red",
1178 + "value": 80
1179 + }
1180 + ]
1181 + }
1182 + },
1183 + "overrides": [
1184 + {
1185 + "matcher": {
1186 + "id": "byName",
1187 + "options": "data_vulnerability_package_name"
1188 + },
1189 + "properties": [
1190 + {
1191 + "id": "displayName",
1192 + "value": "PACKAGE"
1193 + },
1194 + {
1195 + "id": "custom.align"
1196 + }
1197 + ]
1198 + },
1199 + {
1200 + "matcher": {
1201 + "id": "byName",
1202 + "options": "data_vulnerability_package_condition"
1203 + },
1204 + "properties": [
1205 + {
1206 + "id": "displayName",
1207 + "value": "STATUS"
1208 + },
1209 + {
1210 + "id": "unit",
1211 + "value": "short"
1212 + },
1213 + {
1214 + "id": "decimals",
1215 + "value": -1
1216 + },
1217 + {
1218 + "id": "custom.align"
1219 + }
1220 + ]
1221 + },
1222 + {
1223 + "matcher": {
1224 + "id": "byName",
1225 + "options": "data_vulnerability_cve"
1226 + },
1227 + "properties": [
1228 + {
1229 + "id": "displayName",
1230 + "value": "CVE"
1231 + },
1232 + {
1233 + "id": "unit",
1234 + "value": "kbytes"
1235 + },
1236 + {
1237 + "id": "decimals",
1238 + "value": -1
1239 + },
1240 + {
1241 + "id": "custom.align"
1242 + }
1243 + ]
1244 + },
1245 + {
1246 + "matcher": {
1247 + "id": "byName",
1248 + "options": "agent_name"
1249 + },
1250 + "properties": [
1251 + {
1252 + "id": "displayName",
1253 + "value": "AGENT"
1254 + },
1255 + {
1256 + "id": "unit",
1257 + "value": "short"
1258 + },
1259 + {
1260 + "id": "decimals",
1261 + "value": 2
1262 + },
1263 + {
1264 + "id": "custom.align"
1265 + }
1266 + ]
1267 + },
1268 + {
1269 + "matcher": {
1270 + "id": "byName",
1271 + "options": "data_vulnerability_title"
1272 + },
1273 + "properties": [
1274 + {
1275 + "id": "displayName",
1276 + "value": "CVE TITLE"
1277 + },
1278 + {
1279 + "id": "unit",
1280 + "value": "short"
1281 + },
1282 + {
1283 + "id": "decimals",
1284 + "value": 2
1285 + },
1286 + {
1287 + "id": "custom.align"
1288 + }
1289 + ]
1290 + },
1291 + {
1292 + "matcher": {
1293 + "id": "byName",
1294 + "options": "data_vulnerability_severity"
1295 + },
1296 + "properties": [
1297 + {
1298 + "id": "displayName",
1299 + "value": "SEVERITY"
1300 + },
1301 + {
1302 + "id": "unit",
1303 + "value": "short"
1304 + },
1305 + {
1306 + "id": "decimals",
1307 + "value": 2
1308 + },
1309 + {
1310 + "id": "custom.align"
1311 + }
1312 + ]
1313 + },
1314 + {
1315 + "matcher": {
1316 + "id": "byName",
1317 + "options": "epss_epss"
1318 + },
1319 + "properties": [
1320 + {
1321 + "id": "displayName",
1322 + "value": "EPSS"
1323 + }
1324 + ]
1325 + },
1326 + {
1327 + "matcher": {
1328 + "id": "byName",
1329 + "options": "AGENT"
1330 + },
1331 + "properties": [
1332 + {
1333 + "id": "custom.width",
1334 + "value": 183
1335 + }
1336 + ]
1337 + },
1338 + {
1339 + "matcher": {
1340 + "id": "byName",
1341 + "options": "CVE"
1342 + },
1343 + "properties": [
1344 + {
1345 + "id": "custom.width",
1346 + "value": 183
1347 + }
1348 + ]
1349 + },
1350 + {
1351 + "matcher": {
1352 + "id": "byName",
1353 + "options": "timestamp"
1354 + },
1355 + "properties": [
1356 + {
1357 + "id": "custom.width",
1358 + "value": 200
1359 + }
1360 + ]
1361 + },
1362 + {
1363 + "matcher": {
1364 + "id": "byName",
1365 + "options": "SEVERITY"
1366 + },
1367 + "properties": [
1368 + {
1369 + "id": "custom.width",
1370 + "value": 174
1371 + }
1372 + ]
1373 + },
1374 + {
1375 + "matcher": {
1376 + "id": "byName",
1377 + "options": "CVE TITLE"
1378 + },
1379 + "properties": [
1380 + {
1381 + "id": "custom.width",
1382 + "value": 736
1383 + }
1384 + ]
1385 + }
1386 + ]
1387 + },
1388 + "gridPos": {
1389 + "h": 12,
1390 + "w": 24,
1391 + "x": 0,
1392 + "y": 29
1393 + },
1394 + "id": 48,
1395 + "options": {
1396 + "cellHeight": "sm",
1397 + "footer": {
1398 + "countRows": false,
1399 + "fields": "",
1400 + "reducer": ["sum"],
1401 + "show": false
1402 + },
1403 + "showHeader": true,
1404 + "sortBy": []
1405 + },
1406 + "pluginVersion": "10.2.3",
1407 + "targets": [
1408 + {
1409 + "bucketAggs": [],
1410 + "datasource": {
1411 + "type": "grafana-opensearch-datasource",
1412 + "uid": "replace_datasource_uid"
1413 + },
1414 + "metrics": [
1415 + {
1416 + "id": "1",
1417 + "settings": {
1418 + "size": "500"
1419 + },
1420 + "type": "raw_data"
1421 + }
1422 + ],
1423 + "query": "agent.name:$agent_name",
1424 + "refId": "A",
1425 + "timeField": "vulnerability.detected_at"
1426 + }
1427 + ],
1428 + "title": "SYSTEM VULNERABILITIES - DETAILS",
1429 + "transformations": [
1430 + {
1431 + "id": "filterFieldsByName",
1432 + "options": {
1433 + "byVariable": false,
1434 + "include": {
1435 + "names": [
1436 + "agent.name",
1437 + "package.name",
1438 + "vulnerability.description",
1439 + "vulnerability.id",
1440 + "vulnerability.severity",
1441 + "vulnerability.detected_at"
1442 + ]
1443 + }
1444 + }
1445 + },
1446 + {
1447 + "id": "organize",
1448 + "options": {
1449 + "excludeByName": {},
1450 + "includeByName": {},
1451 + "indexByName": {
1452 + "agent.name": 1,
1453 + "package.name": 3,
1454 + "vulnerability.description": 5,
1455 + "vulnerability.detected_at": 0,
1456 + "vulnerability.id": 2,
1457 + "vulnerability.severity": 4
1458 + },
1459 + "renameByName": {
1460 + "agent.name": "AGENT",
1461 + "package.name": "PACKAGE",
1462 + "vulnerability.description": "DESCRIPTION",
1463 + "vulnerability.detected_at": "DETECTED AT",
1464 + "vulnerability.id": "CVE",
1465 + "vulnerability.severity": "SEVERITY"
1466 + }
1467 + }
1468 + }
1469 + ],
1470 + "type": "table"
1471 + },
1472 + {
1473 + "collapsed": true,
1474 + "datasource": {
1475 + "type": "grafana-opensearch-datasource",
1476 + "uid": "replace_datasource_uid"
1477 + },
1478 + "gridPos": {
1479 + "h": 1,
1480 + "w": 24,
1481 + "x": 0,
1482 + "y": 41
1483 + },
1484 + "id": 62,
1485 + "panels": [
1486 + {
1487 + "datasource": {
1488 + "type": "grafana-opensearch-datasource",
1489 + "uid": "replace_datasource_uid"
1490 + },
1491 + "fieldConfig": {
1492 + "defaults": {
1493 + "mappings": [
1494 + {
1495 + "options": {
1496 + "match": "null",
1497 + "result": {
1498 + "text": "N/A"
1499 + }
1500 + },
1501 + "type": "special"
1502 + }
1503 + ],
1504 + "thresholds": {
1505 + "mode": "absolute",
1506 + "steps": [
1507 + {
1508 + "color": "dark-orange"
1509 + }
1510 + ]
1511 + },
1512 + "unit": "short"
1513 + },
1514 + "overrides": []
1515 + },
1516 + "gridPos": {
1517 + "h": 7,
1518 + "w": 4,
1519 + "x": 0,
1520 + "y": 27
1521 + },
1522 + "id": 63,
1523 + "links": [],
1524 + "options": {
1525 + "colorMode": "value",
1526 + "graphMode": "area",
1527 + "justifyMode": "auto",
1528 + "orientation": "horizontal",
1529 + "reduceOptions": {
1530 + "calcs": ["sum"],
1531 + "fields": "",
1532 + "values": false
1533 + },
1534 + "text": {},
1535 + "textMode": "auto"
1536 + },
1537 + "pluginVersion": "10.0.3",
1538 + "targets": [
1539 + {
1540 + "bucketAggs": [
1541 + {
1542 + "field": "timestamp",
1543 + "id": "2",
1544 + "settings": {
1545 + "interval": "auto",
1546 + "min_doc_count": 0,
1547 + "trimEdges": 0
1548 + },
1549 + "type": "date_histogram"
1550 + }
1551 + ],
1552 + "datasource": {
1553 + "type": "grafana-opensearch-datasource",
1554 + "uid": "replace_datasource_uid"
1555 + },
1556 + "metrics": [
1557 + {
1558 + "field": "select field",
1559 + "id": "1",
1560 + "type": "count"
1561 + }
1562 + ],
1563 + "query": "rule_group2:snyk AND agent_name:$agent_name",
1564 + "refId": "A",
1565 + "timeField": "timestamp"
1566 + }
1567 + ],
1568 + "title": "VULNERABILITY EVENTS",
1569 + "type": "stat"
1570 + },
1571 + {
1572 + "columns": [],
1573 + "datasource": {
1574 + "type": "grafana-opensearch-datasource",
1575 + "uid": "replace_datasource_uid"
1576 + },
1577 + "fontSize": "100%",
1578 + "gridPos": {
1579 + "h": 7,
1580 + "w": 8,
1581 + "x": 4,
1582 + "y": 27
1583 + },
1584 + "id": 64,
1585 + "showHeader": true,
1586 + "sort": {
1587 + "col": 0,
1588 + "desc": true
1589 + },
1590 + "styles": [
1591 + {
1592 + "$$hashKey": "object:108",
1593 + "alias": "Time",
1594 + "align": "auto",
1595 + "dateFormat": "YYYY-MM-DD HH:mm:ss",
1596 + "pattern": "Time",
1597 + "type": "date"
1598 + },
1599 + {
1600 + "$$hashKey": "object:109",
1601 + "alias": "",
1602 + "align": "auto",
1603 + "colorMode": "row",
1604 + "colors": ["rgba(50, 172, 45, 0.97)", "rgba(237, 129, 40, 0.89)", "#FA6400"],
1605 + "dateFormat": "YYYY-MM-DD HH:mm:ss",
1606 + "decimals": -1,
1607 + "mappingType": 1,
1608 + "pattern": "Count",
1609 + "thresholds": ["0", "1"],
1610 + "type": "number",
1611 + "unit": "short"
1612 + },
1613 + {
1614 + "$$hashKey": "object:110",
1615 + "alias": "AGENT",
1616 + "align": "auto",
1617 + "colors": ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
1618 + "dateFormat": "YYYY-MM-DD HH:mm:ss",
1619 + "decimals": 2,
1620 + "mappingType": 1,
1621 + "pattern": "agent_name",
1622 + "thresholds": [],
1623 + "type": "number",
1624 + "unit": "short"
1625 + }
1626 + ],
1627 + "targets": [
1628 + {
1629 + "bucketAggs": [
1630 + {
1631 + "fake": true,
1632 + "field": "agent_name",
1633 + "id": "4",
1634 + "settings": {
1635 + "min_doc_count": 1,
1636 + "order": "desc",
1637 + "orderBy": "_term",
1638 + "size": "0"
1639 + },
1640 + "type": "terms"
1641 + }
1642 + ],
1643 + "datasource": {
1644 + "type": "grafana-opensearch-datasource",
1645 + "uid": "replace_datasource_uid"
1646 + },
1647 + "metrics": [
1648 + {
1649 + "field": "select field",
1650 + "id": "1",
1651 + "type": "count"
1652 + }
1653 + ],
1654 + "query": "rule_group2:snyk AND agent_name:$agent_name",
1655 + "refId": "A",
1656 + "timeField": "timestamp"
1657 + }
1658 + ],
1659 + "title": "DOCKER HOST",
1660 + "transform": "table",
1661 + "type": "table-old"
1662 + },
1663 + {
1664 + "datasource": {
1665 + "type": "grafana-opensearch-datasource",
1666 + "uid": "replace_datasource_uid"
1667 + },
1668 + "fieldConfig": {
1669 + "defaults": {
1670 + "color": {
1671 + "mode": "thresholds"
1672 + },
1673 + "custom": {
1674 + "align": "auto",
1675 + "cellOptions": {
1676 + "type": "auto"
1677 + },
1678 + "filterable": false,
1679 + "inspect": false
1680 + },
1681 + "mappings": [],
1682 + "thresholds": {
1683 + "mode": "absolute",
1684 + "steps": [
1685 + {
1686 + "color": "green"
1687 + },
1688 + {
1689 + "color": "red",
1690 + "value": 80
1691 + }
1692 + ]
1693 + }
1694 + },
1695 + "overrides": [
1696 + {
1697 + "matcher": {
1698 + "id": "byName",
1699 + "options": "Time"
1700 + },
1701 + "properties": [
1702 + {
1703 + "id": "displayName",
1704 + "value": "Time"
1705 + },
1706 + {
1707 + "id": "unit",
1708 + "value": "time: YYYY-MM-DD HH:mm:ss"
1709 + },
1710 + {
1711 + "id": "custom.align"
1712 + }
1713 + ]
1714 + },
1715 + {
1716 + "matcher": {
1717 + "id": "byName",
1718 + "options": ""
1719 + },
1720 + "properties": [
1721 + {
1722 + "id": "unit",
1723 + "value": "short"
1724 + },
1725 + {
1726 + "id": "decimals",
1727 + "value": 2
1728 + },
1729 + {
1730 + "id": "custom.align"
1731 + }
1732 + ]
1733 + },
1734 + {
1735 + "matcher": {
1736 + "id": "byName",
1737 + "options": "data_vulnerability_cve"
1738 + },
1739 + "properties": [
1740 + {
1741 + "id": "displayName",
1742 + "value": "CVE"
1743 + },
1744 + {
1745 + "id": "unit",
1746 + "value": "short"
1747 + },
1748 + {
1749 + "id": "decimals",
1750 + "value": -1
1751 + },
1752 + {
1753 + "id": "links",
1754 + "value": [
1755 + {
1756 + "targetBlank": true,
1757 + "title": "NVD - NIST DATABASE",
1758 + "url": "https://nvd.nist.gov/vuln/detail/$__cell"
1759 + }
1760 + ]
1761 + },
1762 + {
1763 + "id": "custom.align",
1764 + "value": "left"
1765 + }
1766 + ]
1767 + },
1768 + {
1769 + "matcher": {
1770 + "id": "byName",
1771 + "options": "Unique Count"
1772 + },
1773 + "properties": [
1774 + {
1775 + "id": "displayName",
1776 + "value": "HITS"
1777 + },
1778 + {
1779 + "id": "unit",
1780 + "value": "short"
1781 + },
1782 + {
1783 + "id": "decimals",
1784 + "value": -1
1785 + },
1786 + {
1787 + "id": "custom.align"
1788 + }
1789 + ]
1790 + }
1791 + ]
1792 + },
1793 + "gridPos": {
1794 + "h": 7,
1795 + "w": 6,
1796 + "x": 12,
1797 + "y": 27
1798 + },
1799 + "id": 71,
1800 + "options": {
1801 + "cellHeight": "sm",
1802 + "footer": {
1803 + "countRows": false,
1804 + "fields": "",
1805 + "reducer": ["sum"],
1806 + "show": false
1807 + },
1808 + "showHeader": true
1809 + },
1810 + "pluginVersion": "10.0.3",
1811 + "targets": [
1812 + {
1813 + "bucketAggs": [
1814 + {
1815 + "fake": true,
1816 + "field": "data_dockerBaseImage",
1817 + "id": "7",
1818 + "settings": {
1819 + "min_doc_count": 1,
1820 + "order": "desc",
1821 + "orderBy": "_term",
1822 + "size": "10"
1823 + },
1824 + "type": "terms"
1825 + }
1826 + ],
1827 + "datasource": {
1828 + "type": "grafana-opensearch-datasource",
1829 + "uid": "replace_datasource_uid"
1830 + },
1831 + "metrics": [
1832 + {
1833 + "field": "data_dockerBaseImage",
1834 + "id": "1",
1835 + "meta": {},
1836 + "settings": {},
1837 + "type": "cardinality"
1838 + }
1839 + ],
1840 + "query": "rule_group2:snyk AND agent_name:$agent_name",
1841 + "refId": "A",
1842 + "timeField": "timestamp"
1843 + }
1844 + ],
1845 + "title": "DOCKER BASE IMAGE",
1846 + "transformations": [
1847 + {
1848 + "id": "merge",
1849 + "options": {
1850 + "reducers": []
1851 + }
1852 + }
1853 + ],
1854 + "type": "table"
1855 + },
1856 + {
1857 + "datasource": {
1858 + "type": "grafana-opensearch-datasource",
1859 + "uid": "replace_datasource_uid"
1860 + },
1861 + "fieldConfig": {
1862 + "defaults": {
1863 + "color": {
1864 + "mode": "thresholds"
1865 + },
1866 + "custom": {
1867 + "align": "auto",
1868 + "cellOptions": {
1869 + "type": "auto"
1870 + },
1871 + "filterable": false,
1872 + "inspect": false
1873 + },
1874 + "mappings": [],
1875 + "thresholds": {
1876 + "mode": "absolute",
1877 + "steps": [
1878 + {
1879 + "color": "green"
1880 + },
1881 + {
1882 + "color": "red",
1883 + "value": 80
1884 + }
1885 + ]
1886 + }
1887 + },
1888 + "overrides": [
1889 + {
1890 + "matcher": {
1891 + "id": "byName",
1892 + "options": "Time"
1893 + },
1894 + "properties": [
1895 + {
1896 + "id": "displayName",
1897 + "value": "Time"
1898 + },
1899 + {
1900 + "id": "unit",
1901 + "value": "time: YYYY-MM-DD HH:mm:ss"
1902 + },
1903 + {
1904 + "id": "custom.align"
1905 + }
1906 + ]
1907 + },
1908 + {
1909 + "matcher": {
1910 + "id": "byName",
1911 + "options": ""
1912 + },
1913 + "properties": [
1914 + {
1915 + "id": "unit",
1916 + "value": "short"
1917 + },
1918 + {
1919 + "id": "decimals",
1920 + "value": 2
1921 + },
1922 + {
1923 + "id": "custom.align"
1924 + }
1925 + ]
1926 + },
1927 + {
1928 + "matcher": {
1929 + "id": "byName",
1930 + "options": "data_vulnerability_cve"
1931 + },
1932 + "properties": [
1933 + {
1934 + "id": "displayName",
1935 + "value": "CVE"
1936 + },
1937 + {
1938 + "id": "unit",
1939 + "value": "short"
1940 + },
1941 + {
1942 + "id": "decimals",
1943 + "value": -1
1944 + },
1945 + {
1946 + "id": "links",
1947 + "value": [
1948 + {
1949 + "targetBlank": true,
1950 + "title": "NVD - NIST DATABASE",
1951 + "url": "https://nvd.nist.gov/vuln/detail/$__cell"
1952 + }
1953 + ]
1954 + },
1955 + {
1956 + "id": "custom.align",
1957 + "value": "left"
1958 + }
1959 + ]
1960 + },
1961 + {
1962 + "matcher": {
1963 + "id": "byName",
1964 + "options": "Unique Count"
1965 + },
1966 + "properties": [
1967 + {
1968 + "id": "displayName",
1969 + "value": "HITS"
1970 + },
1971 + {
1972 + "id": "unit",
1973 + "value": "short"
1974 + },
1975 + {
1976 + "id": "decimals",
1977 + "value": -1
1978 + },
1979 + {
1980 + "id": "custom.align"
1981 + }
1982 + ]
1983 + }
1984 + ]
1985 + },
1986 + "gridPos": {
1987 + "h": 7,
1988 + "w": 6,
1989 + "x": 18,
1990 + "y": 27
1991 + },
1992 + "id": 65,
1993 + "options": {
1994 + "cellHeight": "sm",
1995 + "footer": {
1996 + "countRows": false,
1997 + "fields": "",
1998 + "reducer": ["sum"],
1999 + "show": false
2000 + },
2001 + "showHeader": true
2002 + },
2003 + "pluginVersion": "10.0.3",
2004 + "targets": [
2005 + {
2006 + "bucketAggs": [
2007 + {
2008 + "fake": true,
2009 + "field": "data_identifiers_CVE",
2010 + "id": "7",
2011 + "settings": {
2012 + "min_doc_count": 1,
2013 + "order": "desc",
2014 + "orderBy": "_term",
2015 + "size": "10"
2016 + },
2017 + "type": "terms"
2018 + }
2019 + ],
2020 + "datasource": {
2021 + "type": "grafana-opensearch-datasource",
2022 + "uid": "replace_datasource_uid"
2023 + },
2024 + "metrics": [
2025 + {
2026 + "field": "data_identifiers_CVE",
2027 + "id": "1",
2028 + "meta": {},
2029 + "settings": {},
2030 + "type": "cardinality"
2031 + }
2032 + ],
2033 + "query": "rule_group2:snyk AND agent_name:$agent_name",
2034 + "refId": "A",
2035 + "timeField": "timestamp"
2036 + }
2037 + ],
2038 + "title": "CVEs",
2039 + "transformations": [
2040 + {
2041 + "id": "merge",
2042 + "options": {
2043 + "reducers": []
2044 + }
2045 + }
2046 + ],
2047 + "type": "table"
2048 + },
2049 + {
2050 + "datasource": {
2051 + "type": "grafana-opensearch-datasource",
2052 + "uid": "replace_datasource_uid"
2053 + },
2054 + "fieldConfig": {
2055 + "defaults": {
2056 + "mappings": [],
2057 + "thresholds": {
2058 + "mode": "absolute",
2059 + "steps": [
2060 + {
2061 + "color": "green"
2062 + },
2063 + {
2064 + "color": "red",
2065 + "value": 80
2066 + }
2067 + ]
2068 + }
2069 + },
2070 + "overrides": []
2071 + },
2072 + "gridPos": {
2073 + "h": 11,
2074 + "w": 17,
2075 + "x": 0,
2076 + "y": 34
2077 + },
2078 + "id": 67,
2079 + "options": {
2080 + "displayMode": "gradient",
2081 + "minVizHeight": 10,
2082 + "minVizWidth": 0,
2083 + "orientation": "vertical",
2084 + "reduceOptions": {
2085 + "calcs": ["sum"],
2086 + "fields": "",
2087 + "values": false
2088 + },
2089 + "showUnfilled": true,
2090 + "text": {},
2091 + "valueMode": "color"
2092 + },
2093 + "pluginVersion": "10.0.3",
2094 + "targets": [
2095 + {
2096 + "bucketAggs": [
2097 + {
2098 + "fake": true,
2099 + "field": "data_packageName",
2100 + "id": "6",
2101 + "settings": {
2102 + "min_doc_count": 1,
2103 + "order": "desc",
2104 + "orderBy": "_count",
2105 + "size": "15"
2106 + },
2107 + "type": "terms"
2108 + },
2109 + {
2110 + "fake": true,
2111 + "field": "timestamp",
2112 + "id": "5",
2113 + "settings": {
2114 + "interval": "auto",
2115 + "min_doc_count": 0,
2116 + "trimEdges": 0
2117 + },
2118 + "type": "date_histogram"
2119 + }
2120 + ],
2121 + "datasource": {
2122 + "type": "grafana-opensearch-datasource",
2123 + "uid": "replace_datasource_uid"
2124 + },
2125 + "metrics": [
2126 + {
2127 + "field": "type",
2128 + "id": "1",
2129 + "meta": {},
2130 + "settings": {},
2131 + "type": "count"
2132 + }
2133 + ],
2134 + "query": "rule_group2:snyk AND agent_name:$agent_name",
2135 + "refId": "A",
2136 + "timeField": "timestamp"
2137 + }
2138 + ],
2139 + "title": "VULNERABLE SOFTWARE / PACKAGE",
2140 + "type": "bargauge"
2141 + },
2142 + {
2143 + "datasource": {
2144 + "type": "grafana-opensearch-datasource",
2145 + "uid": "replace_datasource_uid"
2146 + },
2147 + "fieldConfig": {
2148 + "defaults": {
2149 + "color": {
2150 + "mode": "palette-classic"
2151 + },
2152 + "custom": {
2153 + "hideFrom": {
2154 + "legend": false,
2155 + "tooltip": false,
2156 + "viz": false
2157 + }
2158 + },
2159 + "decimals": 0,
2160 + "mappings": [],
2161 + "unit": "short"
2162 + },
2163 + "overrides": [
2164 + {
2165 + "matcher": {
2166 + "id": "byName",
2167 + "options": "Critical"
2168 + },
2169 + "properties": [
2170 + {
2171 + "id": "color",
2172 + "value": {
2173 + "fixedColor": "#C4162A",
2174 + "mode": "fixed"
2175 + }
2176 + }
2177 + ]
2178 + },
2179 + {
2180 + "matcher": {
2181 + "id": "byName",
2182 + "options": "High"
2183 + },
2184 + "properties": [
2185 + {
2186 + "id": "color",
2187 + "value": {
2188 + "fixedColor": "#F2495C",
2189 + "mode": "fixed"
2190 + }
2191 + }
2192 + ]
2193 + },
2194 + {
2195 + "matcher": {
2196 + "id": "byName",
2197 + "options": "Low"
2198 + },
2199 + "properties": [
2200 + {
2201 + "id": "color",
2202 + "value": {
2203 + "fixedColor": "#5794F2",
2204 + "mode": "fixed"
2205 + }
2206 + }
2207 + ]
2208 + },
2209 + {
2210 + "matcher": {
2211 + "id": "byName",
2212 + "options": "Medium"
2213 + },
2214 + "properties": [
2215 + {
2216 + "id": "color",
2217 + "value": {
2218 + "fixedColor": "#FF9830",
2219 + "mode": "fixed"
2220 + }
2221 + }
2222 + ]
2223 + },
2224 + {
2225 + "matcher": {
2226 + "id": "byName",
2227 + "options": "high"
2228 + },
2229 + "properties": [
2230 + {
2231 + "id": "color",
2232 + "value": {
2233 + "fixedColor": "red",
2234 + "mode": "fixed"
2235 + }
2236 + }
2237 + ]
2238 + },
2239 + {
2240 + "matcher": {
2241 + "id": "byName",
2242 + "options": "medium"
2243 + },
2244 + "properties": [
2245 + {
2246 + "id": "color",
2247 + "value": {
2248 + "fixedColor": "orange",
2249 + "mode": "fixed"
2250 + }
2251 + }
2252 + ]
2253 + }
2254 + ]
2255 + },
2256 + "gridPos": {
2257 + "h": 11,
2258 + "w": 7,
2259 + "x": 17,
2260 + "y": 34
2261 + },
2262 + "id": 66,
2263 + "links": [],
2264 + "maxDataPoints": 3,
2265 + "options": {
2266 + "legend": {
2267 + "calcs": [],
2268 + "displayMode": "table",
2269 + "placement": "right",
2270 + "showLegend": true,
2271 + "values": ["value"]
2272 + },
2273 + "pieType": "donut",
2274 + "reduceOptions": {
2275 + "calcs": ["sum"],
2276 + "fields": "",
2277 + "values": false
2278 + },
2279 + "tooltip": {
2280 + "mode": "single",
2281 + "sort": "none"
2282 + }
2283 + },
2284 + "targets": [
2285 + {
2286 + "bucketAggs": [
2287 + {
2288 + "fake": true,
2289 + "field": "data_nvdSeverity",
2290 + "id": "3",
2291 + "settings": {
2292 + "min_doc_count": 1,
2293 + "order": "desc",
2294 + "orderBy": "_count",
2295 + "size": "0"
2296 + },
2297 + "type": "terms"
2298 + },
2299 + {
2300 + "field": "timestamp",
2301 + "id": "2",
2302 + "settings": {
2303 + "interval": "auto",
2304 + "min_doc_count": 0,
2305 + "trimEdges": 0
2306 + },
2307 + "type": "date_histogram"
2308 + }
2309 + ],
2310 + "datasource": {
2311 + "type": "grafana-opensearch-datasource",
2312 + "uid": "replace_datasource_uid"
2313 + },
2314 + "metrics": [
2315 + {
2316 + "field": "select field",
2317 + "id": "1",
2318 + "type": "count"
2319 + }
2320 + ],
2321 + "query": "rule_group2:snyk AND agent_name:$agent_name",
2322 + "refId": "A",
2323 + "timeField": "timestamp"
2324 + }
2325 + ],
2326 + "title": "VULNERABILITY LEVELS",
2327 + "type": "piechart"
2328 + }
2329 + ],
2330 + "title": "DOCKER IMAGES VULNERABILITIES - SUMMARY",
2331 + "type": "row"
2332 + },
2333 + {
2334 + "collapsed": true,
2335 + "datasource": {
2336 + "type": "grafana-opensearch-datasource",
2337 + "uid": "replace_datasource_uid"
2338 + },
2339 + "gridPos": {
2340 + "h": 1,
2341 + "w": 24,
2342 + "x": 0,
2343 + "y": 42
2344 + },
2345 + "id": 70,
2346 + "panels": [
2347 + {
2348 + "datasource": {
2349 + "type": "grafana-opensearch-datasource",
2350 + "uid": "replace_datasource_uid"
2351 + },
2352 + "fieldConfig": {
2353 + "defaults": {
2354 + "color": {
2355 + "mode": "thresholds"
2356 + },
2357 + "custom": {
2358 + "align": "auto",
2359 + "cellOptions": {
2360 + "type": "auto"
2361 + }
2362 + },
2363 + "mappings": [],
2364 + "thresholds": {
2365 + "mode": "absolute",
2366 + "steps": [
2367 + {
2368 + "color": "green"
2369 + },
2370 + {
2371 + "color": "red",
2372 + "value": 80
2373 + }
2374 + ]
2375 + }
2376 + },
2377 + "overrides": [
2378 + {
2379 + "matcher": {
2380 + "id": "byName",
2381 + "options": "data_vulnerability_package_name"
2382 + },
2383 + "properties": [
2384 + {
2385 + "id": "displayName",
2386 + "value": "PACKAGE"
2387 + },
2388 + {
2389 + "id": "custom.align"
2390 + }
2391 + ]
2392 + },
2393 + {
2394 + "matcher": {
2395 + "id": "byName",
2396 + "options": "data_vulnerability_package_condition"
2397 + },
2398 + "properties": [
2399 + {
2400 + "id": "displayName",
2401 + "value": "STATUS"
2402 + },
2403 + {
2404 + "id": "unit",
2405 + "value": "short"
2406 + },
2407 + {
2408 + "id": "decimals",
2409 + "value": -1
2410 + },
2411 + {
2412 + "id": "custom.align"
2413 + }
2414 + ]
2415 + },
2416 + {
2417 + "matcher": {
2418 + "id": "byName",
2419 + "options": "data_vulnerability_cve"
2420 + },
2421 + "properties": [
2422 + {
2423 + "id": "displayName",
2424 + "value": "CVE"
2425 + },
2426 + {
2427 + "id": "unit",
2428 + "value": "kbytes"
2429 + },
2430 + {
2431 + "id": "decimals",
2432 + "value": -1
2433 + },
2434 + {
2435 + "id": "custom.align"
2436 + }
2437 + ]
2438 + },
2439 + {
2440 + "matcher": {
2441 + "id": "byName",
2442 + "options": "agent_name"
2443 + },
2444 + "properties": [
2445 + {
2446 + "id": "displayName",
2447 + "value": "AGENT"
2448 + },
2449 + {
2450 + "id": "unit",
2451 + "value": "short"
2452 + },
2453 + {
2454 + "id": "decimals",
2455 + "value": 2
2456 + },
2457 + {
2458 + "id": "custom.align"
2459 + }
2460 + ]
2461 + },
2462 + {
2463 + "matcher": {
2464 + "id": "byName",
2465 + "options": "data_vulnerability_title"
2466 + },
2467 + "properties": [
2468 + {
2469 + "id": "displayName",
2470 + "value": "CVE TITLE"
2471 + },
2472 + {
2473 + "id": "unit",
2474 + "value": "short"
2475 + },
2476 + {
2477 + "id": "decimals",
2478 + "value": 2
2479 + },
2480 + {
2481 + "id": "custom.align"
2482 + }
2483 + ]
2484 + },
2485 + {
2486 + "matcher": {
2487 + "id": "byName",
2488 + "options": "data_vulnerability_severity"
2489 + },
2490 + "properties": [
2491 + {
2492 + "id": "displayName",
2493 + "value": "SEVERITY"
2494 + },
2495 + {
2496 + "id": "unit",
2497 + "value": "short"
2498 + },
2499 + {
2500 + "id": "decimals",
2501 + "value": 2
2502 + },
2503 + {
2504 + "id": "custom.align"
2505 + }
2506 + ]
2507 + }
2508 + ]
2509 + },
2510 + "gridPos": {
2511 + "h": 12,
2512 + "w": 24,
2513 + "x": 0,
2514 + "y": 46
2515 + },
2516 + "id": 68,
2517 + "options": {
2518 + "footer": {
2519 + "fields": "",
2520 + "reducer": ["sum"],
2521 + "show": false
2522 + },
2523 + "showHeader": true
2524 + },
2525 + "pluginVersion": "8.3.3",
2526 + "targets": [
2527 + {
2528 + "bucketAggs": [],
2529 + "datasource": {
2530 + "type": "grafana-opensearch-datasource",
2531 + "uid": "replace_datasource_uid"
2532 + },
2533 + "metrics": [
2534 + {
2535 + "id": "1",
2536 + "settings": {
2537 + "size": "500"
2538 + },
2539 + "type": "raw_data"
2540 + }
2541 + ],
2542 + "query": "rule_group2:snyk AND agent_name:$agent_name",
2543 + "refId": "A",
2544 + "timeField": "timestamp"
2545 + }
2546 + ],
2547 + "title": "SYSTEM VULNERABILITIES - DETAILS",
2548 + "transformations": [
2549 + {
2550 + "id": "filterFieldsByName",
2551 + "options": {
2552 + "include": {
2553 + "names": [
2554 + "timestamp",
2555 + "agent_name",
2556 + "data_dockerBaseImage",
2557 + "data_name",
2558 + "data_nearestFixedInVersion",
2559 + "data_nvdSeverity",
2560 + "data_malicious",
2561 + "data_identifiers_CWE",
2562 + "data_identifiers_CVE",
2563 + "data_cvssScore"
2564 + ]
2565 + }
2566 + }
2567 + },
2568 + {
2569 + "id": "organize",
2570 + "options": {
2571 + "excludeByName": {},
2572 + "indexByName": {
2573 + "agent_name": 1,
2574 + "data_cvssScore": 7,
2575 + "data_dockerBaseImage": 2,
2576 + "data_identifiers_CVE": 4,
2577 + "data_identifiers_CWE": 5,
2578 + "data_malicious": 8,
2579 + "data_name": 3,
2580 + "data_nearestFixedInVersion": 9,
2581 + "data_nvdSeverity": 6,
2582 + "timestamp": 0
2583 + },
2584 + "renameByName": {
2585 + "agent_name": "DOCKER HOST",
2586 + "data_cvssScore": "SCORE",
2587 + "data_dockerBaseImage": "CONTAINER BASE IMAGE",
2588 + "data_identifiers_CVE": "CVE",
2589 + "data_identifiers_CWE": "CWE",
2590 + "data_malicious": "MALICIOUS",
2591 + "data_name": "IMAGE",
2592 + "data_nearestFixedInVersion": "FIXED IN",
2593 + "data_nvdSeverity": "NVD SEVERITY",
2594 + "timestamp": "DATE/TIME"
2595 + }
2596 + }
2597 + }
2598 + ],
2599 + "transparent": true,
2600 + "type": "table"
2601 + }
2602 + ],
2603 + "title": "DOCKER IMAGES VULNERABILITIES - ENTRIES",
2604 + "type": "row"
2605 + }
2606 + ],
2607 + "refresh": "",
2608 + "schemaVersion": 39,
2609 + "tags": ["EDR"],
2610 + "templating": {
2611 + "list": [
2612 + {
2613 + "current": {
2614 + "selected": false,
2615 + "text": "All",
2616 + "value": "$__all"
2617 + },
2618 + "datasource": {
2619 + "type": "grafana-opensearch-datasource",
2620 + "uid": "replace_datasource_uid"
2621 + },
2622 + "definition": "{ \"find\": \"terms\", \"field\": \"agent_name\", \"query\": \"rule_groups:vulnerability-detector\"}",
2623 + "hide": 0,
2624 + "includeAll": true,
2625 + "label": "Agent",
2626 + "multi": false,
2627 + "name": "agent_name",
2628 + "options": [],
2629 + "query": "{ \"find\": \"terms\", \"field\": \"agent_name\", \"query\": \"rule_groups:vulnerability-detector\"}",
2630 + "refresh": 2,
2631 + "regex": "",
2632 + "skipUrlSync": false,
2633 + "sort": 2,
2634 + "tagValuesQuery": "",
2635 + "tagsQuery": "",
2636 + "type": "query",
2637 + "useTags": false
2638 + },
2639 + {
2640 + "datasource": {
2641 + "type": "grafana-opensearch-datasource",
2642 + "uid": "replace_datasource_uid"
2643 + },
2644 + "filters": [],
2645 + "hide": 0,
2646 + "name": "Filters",
2647 + "skipUrlSync": false,
2648 + "type": "adhoc"
2649 + }
2650 + ]
2651 + },
2652 + "time": {
2653 + "from": "now-30d",
2654 + "to": "now"
2655 + },
2656 + "timepicker": {
2657 + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"],
2658 + "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"]
2659 + },
2660 + "timezone": "",
2661 + "title": "EDR - SYSTEM VULNERABILITIES - WAZUH 4.8 and ABOVE",
2662 + "weekStart": ""
2663 +}
backend/app/connectors/grafana/schema/dashboards.py
+1
@@ -43,6 +43,7 @@ class WazuhDashboard(Enum):
43 EDR_AV_MALWARE_IOC = ("Wazuh", "edr_av_malware_ioc.json")
44 EDR_AGENT_INVENTORY = ("Wazuh", "edr_agent_inventory.json")
45 EDR_AD_INVENOTRY = ("Wazuh", "edr_ad_inventory.json")
46 + EDR_SYSTEM_VULNERABILITIES_NEW = ("Wazuh", "edr_system_vulnerabilities_new.json")
47
48
49 class Office365Dashboard(Enum):
backend/app/customer_provisioning/services/grafana.py
+79
@@ -115,6 +115,85 @@ async def create_grafana_datasource(
115 return GrafanaDataSourceCreationResponse(**results)
116
117
118 +async def create_vulnerability_datasource(
119 + request: ProvisionNewCustomer,
120 + organization_id: int,
121 + session: AsyncSession,
122 +) -> GrafanaDataSourceCreationResponse:
123 + """
124 + Creates a Grafana Wazuh Vulnerabilites datasource for a new customer using the OpenSearch Data Source.
125 + USED WHEN WAZUH VERSION IS 4.8.0 and above
126 +
127 + Args:
128 + request (ProvisionNewCustomer): The request object containing customer information.
129 + organization_id (int): The ID of the organization to create the datasource for.
130 + session (AsyncSession): The database session.
131 +
132 + Returns:
133 + GrafanaDataSourceCreationResponse: The response object containing the result of the datasource creation.
134 + """
135 + logger.info("Creating Grafana datasource")
136 + grafana_client = await create_grafana_client("Grafana")
137 + # Switch to the newly created organization
138 + grafana_client.user.switch_actual_user_organisation(organization_id)
139 + datasource_payload = GrafanaDatasource(
140 + name="VULNERABILITIES",
141 + type="grafana-opensearch-datasource",
142 + typeName="OpenSearch",
143 + access="proxy",
144 + url=await get_connector_attribute(
145 + connector_id=1,
146 + column_name="connector_url",
147 + session=session,
148 + ),
149 + database=f"{request.customer_index_name}*",
150 + basicAuth=True,
151 + basicAuthUser=await get_connector_attribute(
152 + connector_id=1,
153 + column_name="connector_username",
154 + session=session,
155 + ),
156 + secureJsonData={
157 + "basicAuthPassword": await get_connector_attribute(
158 + connector_id=1,
159 + column_name="connector_password",
160 + session=session,
161 + ),
162 + },
163 + isDefault=False,
164 + jsonData={
165 + "dataLinks": [
166 + {"field": "^vulnerability.id$", "url": "https://nvd.nist.gov/vuln/detail/${__value.raw}"},
167 + {
168 + "field": "^_id$",
169 + "url": (
170 + "{}/explore?left=%7B%22datasource%22:%22WAZUH%22,%22queries%22:%5B%7B"
171 + "%22refId%22:%22A%22,%22query%22:%22_id:${{__value.raw}}%22,%22alias%22:%22%22,"
172 + "%22metrics%22:%5B%7B%22id%22:%221%22,%22type%22:%22logs%22,%22settings%22:"
173 + "%7B%22limit%22:%22500%22%7D%7D%5D,%22bucketAggs%22:%5B%5D,%22timeField%22:"
174 + "%22timestamp%22%7D%5D,%22range%22:%7B%22from%22:%22now-6h%22,%22to%22:%22now%22%7D%7D"
175 + ).format(request.grafana_url),
176 + },
177 + ],
178 + "database": "wazuh-states-vulnerabilities*",
179 + "flavor": "opensearch",
180 + "includeFrozen": False,
181 + "logLevelField": "vulnerability.score.base",
182 + "logMessageField": "vulnerability.id",
183 + "maxConcurrentShardRequests": 5,
184 + "pplEnabled": True,
185 + "timeField": "timestamp",
186 + "tlsSkipVerify": True,
187 + "version": await get_opensearch_version(),
188 + },
189 + readOnly=True,
190 + )
191 + results = grafana_client.datasource.create_datasource(
192 + datasource=datasource_payload.dict(),
193 + )
194 + return GrafanaDataSourceCreationResponse(**results)
195 +
196 +
197 async def create_grafana_folder(
198 organization_id: int,
199 folder_title: str,
backend/app/customer_provisioning/services/provision.py
+11
@@ -5,6 +5,7 @@ from fastapi import HTTPException
5 from loguru import logger
6 from sqlalchemy.ext.asyncio import AsyncSession
7
8 +from app.agents.routes.agents import check_wazuh_manager_version
9 from app.connectors.dfir_iris.utils.universal import verify_dfir_iris_connection
10 from app.connectors.grafana.schema.dashboards import DashboardProvisionRequest
11 from app.connectors.grafana.services.dashboards import provision_dashboards
@@ -24,6 +25,7 @@ from app.customer_provisioning.services.dfir_iris import create_customer
25 from app.customer_provisioning.services.grafana import create_grafana_datasource
26 from app.customer_provisioning.services.grafana import create_grafana_folder
27 from app.customer_provisioning.services.grafana import create_grafana_organization
28 +from app.customer_provisioning.services.grafana import create_vulnerability_datasource
29 from app.customer_provisioning.services.graylog import connect_stream_to_pipeline
30 from app.customer_provisioning.services.graylog import create_event_stream
31 from app.customer_provisioning.services.graylog import create_index_set
@@ -103,6 +105,15 @@ async def provision_wazuh_customer(
105 session=session,
106 )
107 ).datasource.uid
108 + # ! CREATE THE VULNERABILITY DATASOURCE IF WAZUH VERSION 4.8.0 OR HIGHER ! #
109 + if check_wazuh_manager_version() is True:
110 + logger.info("Creating vulnerability datasource since Wazuh version is 4.8.0 or higher")
111 + await create_vulnerability_datasource(
112 + request=request,
113 + organization_id=provision_meta_data["grafana_organization_id"],
114 + session=session,
115 + )
116 + logger.info("Creating EDR folder and dashboards")
117 provision_meta_data["grafana_edr_folder_id"] = (
118 await create_grafana_folder(
119 organization_id=provision_meta_data["grafana_organization_id"],
backend/app/integrations/duo/services/provision.py
+1 -1
@@ -50,7 +50,7 @@ async def build_index_set_config(
50 return TimeBasedIndexSet(
51 title=f"{(await get_customer(customer_code, session)).customer.customer_name} - DUO",
52 description=f"{customer_code} - DUO",
53 - index_prefix=f"duo-{customer_code}",
53 + index_prefix=f"duo-{customer_code.lower()}-",
54 rotation_strategy_class="org.graylog2.indexer.rotation.strategies.TimeBasedRotationStrategy",
55 rotation_strategy={
56 "type": "org.graylog2.indexer.rotation.strategies.TimeBasedRotationStrategyConfig",
frontend/figma-tokens.json
+110 -30
@@ -57,39 +57,39 @@
57 "type": "color"
58 },
59 "color-light-primary": {
60 - "value": "rgb(0, 178, 123)",
60 + "value": "rgb(255, 182, 0)",
61 "type": "color"
62 },
63 "color-light-primary-005": {
64 - "value": "rgba(0, 178, 123, 0.05)",
64 + "value": "rgba(255, 182, 0, 0.05)",
65 "type": "color"
66 },
67 "color-light-primary-010": {
68 - "value": "rgba(0, 178, 123, 0.1)",
68 + "value": "rgba(255, 182, 0, 0.1)",
69 "type": "color"
70 },
71 "color-light-primary-015": {
72 - "value": "rgba(0, 178, 123, 0.15)",
72 + "value": "rgba(255, 182, 0, 0.15)",
73 "type": "color"
74 },
75 "color-light-primary-020": {
76 - "value": "rgba(0, 178, 123, 0.2)",
76 + "value": "rgba(255, 182, 0, 0.2)",
77 "type": "color"
78 },
79 "color-light-primary-030": {
80 - "value": "rgba(0, 178, 123, 0.3)",
80 + "value": "rgba(255, 182, 0, 0.3)",
81 "type": "color"
82 },
83 "color-light-primary-040": {
84 - "value": "rgba(0, 178, 123, 0.4)",
84 + "value": "rgba(255, 182, 0, 0.4)",
85 "type": "color"
86 },
87 "color-light-primary-050": {
88 - "value": "rgba(0, 178, 123, 0.5)",
88 + "value": "rgba(255, 182, 0, 0.5)",
89 "type": "color"
90 },
91 "color-light-primary-060": {
92 - "value": "rgba(0, 178, 123, 0.6)",
92 + "value": "rgba(255, 182, 0, 0.6)",
93 "type": "color"
94 },
95 "color-light-info": {
@@ -101,13 +101,29 @@
101 "type": "color"
102 },
103 "color-light-warning": {
104 - "value": "#FFB600",
104 + "value": "#E3C22F",
105 "type": "color"
106 },
107 "color-light-error": {
108 "value": "#FF0156",
109 "type": "color"
110 },
111 + "color-light-info-005": {
112 + "value": "rgba(98, 103, 255, 0.05)",
113 + "type": "color"
114 + },
115 + "color-light-success-005": {
116 + "value": "rgba(0, 178, 123, 0.05)",
117 + "type": "color"
118 + },
119 + "color-light-warning-005": {
120 + "value": "rgba(227, 194, 47, 0.05)",
121 + "type": "color"
122 + },
123 + "color-light-error-005": {
124 + "value": "rgba(255, 1, 86, 0.05)",
125 + "type": "color"
126 + },
127 "color-light-secondary-1": {
128 "value": "rgb(98, 103, 255)",
129 "type": "color"
@@ -149,23 +165,23 @@
165 "type": "color"
166 },
167 "color-light-secondary-3": {
152 - "value": "rgb(255, 182, 0)",
168 + "value": "rgb(227, 194, 47)",
169 "type": "color"
170 },
171 "color-light-secondary-3-opacity-005": {
156 - "value": "rgba(255, 182, 0, 0.05)",
172 + "value": "rgba(227, 194, 47, 0.05)",
173 "type": "color"
174 },
175 "color-light-secondary-3-opacity-010": {
160 - "value": "rgba(255, 182, 0, 0.1)",
176 + "value": "rgba(227, 194, 47, 0.1)",
177 "type": "color"
178 },
179 "color-light-secondary-3-opacity-020": {
164 - "value": "rgba(255, 182, 0, 0.2)",
180 + "value": "rgba(227, 194, 47, 0.2)",
181 "type": "color"
182 },
183 "color-light-secondary-3-opacity-030": {
168 - "value": "rgba(255, 182, 0, 0.3)",
184 + "value": "rgba(227, 194, 47, 0.3)",
185 "type": "color"
186 },
187 "color-light-secondary-4": {
@@ -200,6 +216,10 @@
216 "value": "rgba(0, 0, 0, 0.2)",
217 "type": "color"
218 },
219 + "color-light-divider-030": {
220 + "value": "rgba(0, 0, 0, 0.3)",
221 + "type": "color"
222 + },
223 "color-light-hover-005": {
224 "value": "rgba(0, 0, 0, 0.05)",
225 "type": "color"
@@ -237,39 +257,39 @@
257 "type": "color"
258 },
259 "color-dark-primary": {
240 - "value": "rgb(0, 225, 155)",
260 + "value": "rgb(255, 182, 0)",
261 "type": "color"
262 },
263 "color-dark-primary-005": {
244 - "value": "rgba(0, 225, 155, 0.05)",
264 + "value": "rgba(255, 182, 0, 0.05)",
265 "type": "color"
266 },
267 "color-dark-primary-010": {
248 - "value": "rgba(0, 225, 155, 0.1)",
268 + "value": "rgba(255, 182, 0, 0.1)",
269 "type": "color"
270 },
271 "color-dark-primary-015": {
252 - "value": "rgba(0, 225, 155, 0.15)",
272 + "value": "rgba(255, 182, 0, 0.15)",
273 "type": "color"
274 },
275 "color-dark-primary-020": {
256 - "value": "rgba(0, 225, 155, 0.2)",
276 + "value": "rgba(255, 182, 0, 0.2)",
277 "type": "color"
278 },
279 "color-dark-primary-030": {
260 - "value": "rgba(0, 225, 155, 0.3)",
280 + "value": "rgba(255, 182, 0, 0.3)",
281 "type": "color"
282 },
283 "color-dark-primary-040": {
264 - "value": "rgba(0, 225, 155, 0.4)",
284 + "value": "rgba(255, 182, 0, 0.4)",
285 "type": "color"
286 },
287 "color-dark-primary-050": {
268 - "value": "rgba(0, 225, 155, 0.5)",
288 + "value": "rgba(255, 182, 0, 0.5)",
289 "type": "color"
290 },
291 "color-dark-primary-060": {
272 - "value": "rgba(0, 225, 155, 0.6)",
292 + "value": "rgba(255, 182, 0, 0.6)",
293 "type": "color"
294 },
295 "color-dark-info": {
@@ -281,13 +301,29 @@
301 "type": "color"
302 },
303 "color-dark-warning": {
284 - "value": "#FFB600",
304 + "value": "#E3C22F",
305 "type": "color"
306 },
307 "color-dark-error": {
308 "value": "#FF0156",
309 "type": "color"
310 },
311 + "color-dark-info-005": {
312 + "value": "rgba(98, 103, 255, 0.05)",
313 + "type": "color"
314 + },
315 + "color-dark-success-005": {
316 + "value": "rgba(0, 178, 123, 0.05)",
317 + "type": "color"
318 + },
319 + "color-dark-warning-005": {
320 + "value": "rgba(227, 194, 47, 0.05)",
321 + "type": "color"
322 + },
323 + "color-dark-error-005": {
324 + "value": "rgba(255, 1, 86, 0.05)",
325 + "type": "color"
326 + },
327 "color-dark-secondary-1": {
328 "value": "rgb(98, 103, 255)",
329 "type": "color"
@@ -329,23 +365,23 @@
365 "type": "color"
366 },
367 "color-dark-secondary-3": {
332 - "value": "rgb(255, 182, 0)",
368 + "value": "rgb(227, 194, 47)",
369 "type": "color"
370 },
371 "color-dark-secondary-3-opacity-005": {
336 - "value": "rgba(255, 182, 0, 0.05)",
372 + "value": "rgba(227, 194, 47, 0.05)",
373 "type": "color"
374 },
375 "color-dark-secondary-3-opacity-010": {
340 - "value": "rgba(255, 182, 0, 0.1)",
376 + "value": "rgba(227, 194, 47, 0.1)",
377 "type": "color"
378 },
379 "color-dark-secondary-3-opacity-020": {
344 - "value": "rgba(255, 182, 0, 0.2)",
380 + "value": "rgba(227, 194, 47, 0.2)",
381 "type": "color"
382 },
383 "color-dark-secondary-3-opacity-030": {
348 - "value": "rgba(255, 182, 0, 0.3)",
384 + "value": "rgba(227, 194, 47, 0.3)",
385 "type": "color"
386 },
387 "color-dark-secondary-4": {
@@ -380,6 +416,10 @@
416 "value": "rgba(255, 255, 255, 0.2)",
417 "type": "color"
418 },
419 + "color-dark-divider-030": {
420 + "value": "rgba(255, 255, 255, 0.3)",
421 + "type": "color"
422 + },
423 "color-dark-hover-005": {
424 "value": "rgba(255, 255, 255, 0.05)",
425 "type": "color"
@@ -532,6 +572,22 @@
572 "value": "{color-light-error}",
573 "type": "color"
574 },
575 + "color-info-005": {
576 + "value": "{color-light-info-005}",
577 + "type": "color"
578 + },
579 + "color-success-005": {
580 + "value": "{color-light-success-005}",
581 + "type": "color"
582 + },
583 + "color-warning-005": {
584 + "value": "{color-light-warning-005}",
585 + "type": "color"
586 + },
587 + "color-error-005": {
588 + "value": "{color-light-error-005}",
589 + "type": "color"
590 + },
591 "color-secondary-1": {
592 "value": "{color-light-secondary-1}",
593 "type": "color"
@@ -624,6 +680,10 @@
680 "value": "{color-light-divider-020}",
681 "type": "color"
682 },
683 + "color-divider-030": {
684 + "value": "{color-light-divider-030}",
685 + "type": "color"
686 + },
687 "color-hover-005": {
688 "value": "{color-light-hover-005}",
689 "type": "color"
@@ -714,6 +774,22 @@
774 "value": "{color-dark-error}",
775 "type": "color"
776 },
777 + "color-info-005": {
778 + "value": "{color-dark-info-005}",
779 + "type": "color"
780 + },
781 + "color-success-005": {
782 + "value": "{color-dark-success-005}",
783 + "type": "color"
784 + },
785 + "color-warning-005": {
786 + "value": "{color-dark-warning-005}",
787 + "type": "color"
788 + },
789 + "color-error-005": {
790 + "value": "{color-dark-error-005}",
791 + "type": "color"
792 + },
793 "color-secondary-1": {
794 "value": "{color-dark-secondary-1}",
795 "type": "color"
@@ -806,6 +882,10 @@
882 "value": "{color-dark-divider-020}",
883 "type": "color"
884 },
885 + "color-divider-030": {
886 + "value": "{color-dark-divider-030}",
887 + "type": "color"
888 + },
889 "color-hover-005": {
890 "value": "{color-dark-hover-005}",
891 "type": "color"
frontend/package-lock.json
+143 -149
@@ -14,7 +14,7 @@
14 "@fontsource/lexend": "^5.0.20",
15 "@fontsource/public-sans": "^5.0.18",
16 "@popperjs/core": "^2.11.8",
17 - "@shikijs/markdown-it": "^1.7.0",
17 + "@shikijs/markdown-it": "^1.9.0",
18 "@vueuse/components": "^10.11.0",
19 "@vueuse/core": "^10.11.0",
20 "apexcharts": "^3.49.1",
@@ -34,14 +34,14 @@
34 "password-validator": "^5.3.0",
35 "pinia": "^2.1.7",
36 "pinia-plugin-persistedstate": "^3.2.1",
37 - "secure-ls": "^1.2.6",
38 - "shiki": "^1.7.0",
37 + "secure-ls": "^2.0.0",
38 + "shiki": "^1.9.0",
39 "validator": "^13.12.0",
40 - "vue": "^3.4.29",
40 + "vue": "^3.4.30",
41 "vue-advanced-cropper": "^2.8.9",
42 "vue-highlight-words": "^3.0.1",
43 "vue-i18n": "^9.13.1",
44 - "vue-router": "^4.3.3",
44 + "vue-router": "^4.4.0",
45 "vue-sjv": "^0.0.6",
46 "vue3-apexcharts": "^1.5.3",
47 "vue3-marquee": "^4.2.0",
@@ -50,7 +50,6 @@
50 "devDependencies": {
51 "@clack/prompts": "^0.7.0",
52 "@iconify/vue": "^4.1.2",
53 - "@rushstack/eslint-patch": "^1.10.3",
53 "@tsconfig/node20": "^20.1.4",
54 "@types/bytes": "^3.1.4",
55 "@types/file-saver": "^2.0.7",
@@ -58,7 +57,7 @@
57 "@types/inquirer": "^9.0.7",
58 "@types/jsdom": "^21.1.7",
59 "@types/lodash": "^4.17.5",
61 - "@types/node": "^20.14.5",
60 + "@types/node": "^20.14.8",
61 "@types/validator": "^13.12.0",
62 "@vitejs/plugin-vue": "^5.0.5",
63 "@vitejs/plugin-vue-jsx": "^4.0.0",
@@ -67,7 +66,7 @@
66 "@vue/test-utils": "^2.4.6",
67 "@vue/tsconfig": "^0.5.1",
68 "autoprefixer": "^10.4.19",
70 - "cypress": "^13.11.0",
69 + "cypress": "^13.12.0",
70 "eslint": "^9.5.0",
71 "eslint-plugin-cypress": "^3.3.0",
72 "eslint-plugin-vue": "^9.26.0",
@@ -82,7 +81,7 @@
81 "prettier": "^3.3.2",
82 "sass": "^1.77.6",
83 "start-server-and-test": "^2.0.4",
85 - "tailwind-config-viewer": "^2.0.3",
84 + "tailwind-config-viewer": "^2.0.4",
85 "tailwindcss": "^3.4.4",
86 "taze": "^0.13.8",
87 "type-fest": "^4.20.1",
@@ -90,10 +89,10 @@
89 "vite": "^5.3.1",
90 "vite-bundle-analyzer": "^0.10.3",
91 "vite-bundle-visualizer": "^1.2.1",
93 - "vite-plugin-vue-devtools": "^7.3.1",
92 + "vite-plugin-vue-devtools": "^7.3.4",
93 "vite-svg-loader": "^5.1.0",
94 "vitest": "^1.6.0",
96 - "vue-tsc": "^2.0.21"
95 + "vue-tsc": "^2.0.22"
96 },
97 "engines": {
98 "node": ">=18.0.0"
@@ -2045,33 +2044,27 @@
2044 "win32"
2045 ]
2046 },
2048 - "node_modules/@rushstack/eslint-patch": {
2049 - "version": "1.10.3",
2050 - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.3.tgz",
2051 - "integrity": "sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg==",
2052 - "dev": true
2053 - },
2047 "node_modules/@shikijs/core": {
2055 - "version": "1.7.0",
2056 - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.7.0.tgz",
2057 - "integrity": "sha512-O6j27b7dGmJbR3mjwh/aHH8Ld+GQvA0OQsNO43wKWnqbAae3AYXrhFyScHGX8hXZD6vX2ngjzDFkZY5srtIJbQ=="
2048 + "version": "1.9.0",
2049 + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.9.0.tgz",
2050 + "integrity": "sha512-cbSoY8P/jgGByG8UOl3jnP/CWg/Qk+1q+eAKWtcrU3pNoILF8wTsLB0jT44qUBV8Ce1SvA9uqcM9Xf+u3fJFBw=="
2051 },
2052 "node_modules/@shikijs/markdown-it": {
2060 - "version": "1.7.0",
2061 - "resolved": "https://registry.npmjs.org/@shikijs/markdown-it/-/markdown-it-1.7.0.tgz",
2062 - "integrity": "sha512-R/FdTmvynOjkiGDCIypmBIEunUVpMegk4x+C/weFQnfK5vQekBQfRZu/7sMBzhBZTncBcafhAQDK7QiNkOvwjQ==",
2053 + "version": "1.9.0",
2054 + "resolved": "https://registry.npmjs.org/@shikijs/markdown-it/-/markdown-it-1.9.0.tgz",
2055 + "integrity": "sha512-y1DUEL0wf0UZ30dW1NBx4L9uQs2RN4x5gEZpM7t2/LmMGbJ7d2Hr8mzBlSU9+RWxqSirWX7e6av0tJLhcmd/Hw==",
2056 "dependencies": {
2064 - "@shikijs/transformers": "1.7.0",
2057 + "@shikijs/transformers": "1.9.0",
2058 "markdown-it": "^14.1.0",
2066 - "shiki": "1.7.0"
2059 + "shiki": "1.9.0"
2060 }
2061 },
2062 "node_modules/@shikijs/transformers": {
2070 - "version": "1.7.0",
2071 - "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-1.7.0.tgz",
2072 - "integrity": "sha512-QX3TP+CS4yYLt4X4Dk7wT0MsC7yweTYHMAAKY+ay+uuR9yRdFae/h+hivny2O+YixJHfZl57xtiZfWSrHdyVhQ==",
2063 + "version": "1.9.0",
2064 + "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-1.9.0.tgz",
2065 + "integrity": "sha512-wo8dNbZtFtVhKtw8BnXIT/FDTGMwEdWcQSIRa78ou14JGkMYxSCBN942W5+IRUifP5BwVUWgkXBYX/M3FUFkeg==",
2066 "dependencies": {
2074 - "shiki": "1.7.0"
2067 + "shiki": "1.9.0"
2068 }
2069 },
2070 "node_modules/@sideway/address": {
@@ -2198,9 +2191,9 @@
2191 }
2192 },
2193 "node_modules/@types/node": {
2201 - "version": "20.14.5",
2202 - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.5.tgz",
2203 - "integrity": "sha512-aoRR+fJkZT2l0aGOJhuA8frnCSoNX6W7U2mpNq63+BxBIj5BQFt8rHy627kijCmm63ijdSdwvGgpUsU6MBsZZA==",
2194 + "version": "20.14.8",
2195 + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.8.tgz",
2196 + "integrity": "sha512-DO+2/jZinXfROG7j7WKFn/3C6nFwxy2lLpgLjEXJz+0XKphZlTLJ14mo8Vfg8X5BWN6XjyESXq+LcYdT7tR3bA==",
2197 "dev": true,
2198 "dependencies": {
2199 "undici-types": "~5.26.4"
@@ -2948,30 +2941,27 @@
2941 }
2942 },
2943 "node_modules/@volar/language-core": {
2951 - "version": "2.3.0",
2952 - "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.3.0.tgz",
2953 - "integrity": "sha512-pvhL24WUh3VDnv7Yw5N1sjhPtdx7q9g+Wl3tggmnkMcyK8GcCNElF2zHiKznryn0DiUGk+eez/p2qQhz+puuHw==",
2944 + "version": "2.3.3",
2945 + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.3.3.tgz",
2946 + "integrity": "sha512-Blu4NQaRszEsgK/QvWFRSQPRAhPDbYef+peFV9Gb86swxRCs01q7h673/HYstaPsTbjh/F5mXjozoOFxQ8tLYw==",
2947 "dev": true,
2948 "dependencies": {
2956 - "@volar/source-map": "2.3.0"
2949 + "@volar/source-map": "2.3.3"
2950 }
2951 },
2952 "node_modules/@volar/source-map": {
2960 - "version": "2.3.0",
2961 - "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.3.0.tgz",
2962 - "integrity": "sha512-G/228aZjAOGhDjhlyZ++nDbKrS9uk+5DMaEstjvzglaAw7nqtDyhnQAsYzUg6BMP9BtwZ59RIw5HGePrutn00Q==",
2963 - "dev": true,
2964 - "dependencies": {
2965 - "muggle-string": "^0.4.0"
2966 - }
2953 + "version": "2.3.3",
2954 + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.3.3.tgz",
2955 + "integrity": "sha512-eFRHA13hxiGPt+Xa0EX3yNd50ozctnW5KzQj/IllKmSK/KuBEkSAsIhBdOxCpv1YhV4GmI3iKG9peOs6k9LtnA==",
2956 + "dev": true
2957 },
2958 "node_modules/@volar/typescript": {
2969 - "version": "2.3.0",
2970 - "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.3.0.tgz",
2971 - "integrity": "sha512-PtUwMM87WsKVeLJN33GSTUjBexlKfKgouWlOUIv7pjrOnTwhXHZNSmpc312xgXdTjQPpToK6KXSIcKu9sBQ5LQ==",
2959 + "version": "2.3.3",
2960 + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.3.3.tgz",
2961 + "integrity": "sha512-cwGMSwqIR54Hngu5dYmUJPHFpo6KGj7BlpwT2G9WTKwEjFlH4g/0IPeq064428DAi/VMP42vqAQSBiEHi5Gc4A==",
2962 "dev": true,
2963 "dependencies": {
2974 - "@volar/language-core": "2.3.0",
2964 + "@volar/language-core": "2.3.3",
2965 "path-browserify": "^1.0.1",
2966 "vscode-uri": "^3.0.8"
2967 }
@@ -3050,36 +3040,36 @@
3040 }
3041 },
3042 "node_modules/@vue/compiler-core": {
3053 - "version": "3.4.29",
3054 - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.29.tgz",
3055 - "integrity": "sha512-TFKiRkKKsRCKvg/jTSSKK7mYLJEQdUiUfykbG49rubC9SfDyvT2JrzTReopWlz2MxqeLyxh9UZhvxEIBgAhtrg==",
3043 + "version": "3.4.30",
3044 + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.30.tgz",
3045 + "integrity": "sha512-ZL8y4Xxdh8O6PSwfdZ1IpQ24PjTAieOz3jXb/MDTfDtANcKBMxg1KLm6OX2jofsaQGYfIVzd3BAG22i56/cF1w==",
3046 "dependencies": {
3047 "@babel/parser": "^7.24.7",
3058 - "@vue/shared": "3.4.29",
3048 + "@vue/shared": "3.4.30",
3049 "entities": "^4.5.0",
3050 "estree-walker": "^2.0.2",
3051 "source-map-js": "^1.2.0"
3052 }
3053 },
3054 "node_modules/@vue/compiler-dom": {
3065 - "version": "3.4.29",
3066 - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.29.tgz",
3067 - "integrity": "sha512-A6+iZ2fKIEGnfPJejdB7b1FlJzgiD+Y/sxxKwJWg1EbJu6ZPgzaPQQ51ESGNv0CP6jm6Z7/pO6Ia8Ze6IKrX7w==",
3055 + "version": "3.4.30",
3056 + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.30.tgz",
3057 + "integrity": "sha512-+16Sd8lYr5j/owCbr9dowcNfrHd+pz+w2/b5Lt26Oz/kB90C9yNbxQ3bYOvt7rI2bxk0nqda39hVcwDFw85c2Q==",
3058 "dependencies": {
3069 - "@vue/compiler-core": "3.4.29",
3070 - "@vue/shared": "3.4.29"
3059 + "@vue/compiler-core": "3.4.30",
3060 + "@vue/shared": "3.4.30"
3061 }
3062 },
3063 "node_modules/@vue/compiler-sfc": {
3074 - "version": "3.4.29",
3075 - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.29.tgz",
3076 - "integrity": "sha512-zygDcEtn8ZimDlrEQyLUovoWgKQic6aEQqRXce2WXBvSeHbEbcAsXyCk9oG33ZkyWH4sl9D3tkYc1idoOkdqZQ==",
3064 + "version": "3.4.30",
3065 + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.30.tgz",
3066 + "integrity": "sha512-8vElKklHn/UY8+FgUFlQrYAPbtiSB2zcgeRKW7HkpSRn/JjMRmZvuOtwDx036D1aqKNSTtXkWRfqx53Qb+HmMg==",
3067 "dependencies": {
3068 "@babel/parser": "^7.24.7",
3079 - "@vue/compiler-core": "3.4.29",
3080 - "@vue/compiler-dom": "3.4.29",
3081 - "@vue/compiler-ssr": "3.4.29",
3082 - "@vue/shared": "3.4.29",
3069 + "@vue/compiler-core": "3.4.30",
3070 + "@vue/compiler-dom": "3.4.30",
3071 + "@vue/compiler-ssr": "3.4.30",
3072 + "@vue/shared": "3.4.30",
3073 "estree-walker": "^2.0.2",
3074 "magic-string": "^0.30.10",
3075 "postcss": "^8.4.38",
@@ -3087,12 +3077,12 @@
3077 }
3078 },
3079 "node_modules/@vue/compiler-ssr": {
3090 - "version": "3.4.29",
3091 - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.29.tgz",
3092 - "integrity": "sha512-rFbwCmxJ16tDp3N8XCx5xSQzjhidYjXllvEcqX/lopkoznlNPz3jyy0WGJCyhAaVQK677WWFt3YO/WUEkMMUFQ==",
3080 + "version": "3.4.30",
3081 + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.30.tgz",
3082 + "integrity": "sha512-ZJ56YZGXJDd6jky4mmM0rNaNP6kIbQu9LTKZDhcpddGe/3QIalB1WHHmZ6iZfFNyj5mSypTa4+qDJa5VIuxMSg==",
3083 "dependencies": {
3094 - "@vue/compiler-dom": "3.4.29",
3095 - "@vue/shared": "3.4.29"
3084 + "@vue/compiler-dom": "3.4.30",
3085 + "@vue/shared": "3.4.30"
3086 }
3087 },
3088 "node_modules/@vue/devtools-api": {
@@ -3101,44 +3091,44 @@
3091 "integrity": "sha512-0MiMsFma/HqA6g3KLKn+AGpL1kgKhFWszC9U29NfpWK5LE7bjeXxySWJrOJ77hBz+TBrBQ7o4QJqbPbqbs8rJw=="
3092 },
3093 "node_modules/@vue/devtools-core": {
3104 - "version": "7.3.1",
3105 - "resolved": "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-7.3.1.tgz",
3106 - "integrity": "sha512-5q+q4s3vop3HqSM0+sOVGhT4zEPlI9Cbo2u8sjjizWarBWM4r1FK+SRC2I2Yeg61xxcNViQ1+tOVF3QLNY7WuQ==",
3094 + "version": "7.3.4",
3095 + "resolved": "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-7.3.4.tgz",
3096 + "integrity": "sha512-HiaskdszC1ajYhZkglBzc4BBb4OHAhYonoOaQMx62ZId++ezpd4n0CbM9SXYp8o1JhxImSdueKgJgtyvhm4vAA==",
3097 "dev": true,
3098 "dependencies": {
3109 - "@vue/devtools-kit": "^7.3.1",
3110 - "@vue/devtools-shared": "^7.3.1",
3099 + "@vue/devtools-kit": "^7.3.4",
3100 + "@vue/devtools-shared": "^7.3.4",
3101 "mitt": "^3.0.1",
3102 "nanoid": "^3.3.4",
3103 "pathe": "^1.1.2",
3104 "vite-hot-client": "^0.2.3"
3105 + },
3106 + "peerDependencies": {
3107 + "vue": "^3.0.0"
3108 }
3109 },
3110 "node_modules/@vue/devtools-kit": {
3118 - "version": "7.3.1",
3119 - "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.3.1.tgz",
3120 - "integrity": "sha512-O9LXPo0kC/PGFBRBMvT5PHiSXr6ZdJTshVuGQ1L2mnIZlIBdTjdtCRYQg/OMcEA5JDJPeJT8bnDYBBUbssGjoA==",
3111 + "version": "7.3.4",
3112 + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.3.4.tgz",
3113 + "integrity": "sha512-DalQZWaFLRyA4qfKT0WT7e+q2AwvYoTwd0pWqswHqcpviXw+oU6FlSJHMrEACB3lBHjN1KBS9Kh527sWIe1vcg==",
3114 "dev": true,
3115 "dependencies": {
3123 - "@vue/devtools-shared": "^7.3.1",
3116 + "@vue/devtools-shared": "^7.3.4",
3117 "birpc": "^0.2.17",
3118 "hookable": "^5.5.3",
3119 "mitt": "^3.0.1",
3120 "perfect-debounce": "^1.0.0",
3121 "speakingurl": "^14.0.1",
3122 "superjson": "^2.2.1"
3130 - },
3131 - "peerDependencies": {
3132 - "vue": "^3.0.0"
3123 }
3124 },
3125 "node_modules/@vue/devtools-shared": {
3136 - "version": "7.3.1",
3137 - "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.3.1.tgz",
3138 - "integrity": "sha512-TYbJLZwBy5+SliE095T+V0IMwRu+oP7I2KAsDuNtSsjHKITZvrhz2fNEvik9NsTLcEQBBoX81NvdgpoB29JAog==",
3126 + "version": "7.3.4",
3127 + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.3.4.tgz",
3128 + "integrity": "sha512-5S5cHh7oWLZdboujnLteR3rT8UGfKHfA34aGLyFRB/B5TqBxmeLW1Rq32xW6TCDEy4isoYsYHGwJVp6DQcpiDA==",
3129 "dev": true,
3130 "dependencies": {
3141 - "rfdc": "^1.3.1"
3131 + "rfdc": "^1.4.1"
3132 }
3133 },
3134 "node_modules/@vue/eslint-config-prettier": {
@@ -3180,16 +3170,17 @@
3170 }
3171 },
3172 "node_modules/@vue/language-core": {
3183 - "version": "2.0.21",
3184 - "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.0.21.tgz",
3185 - "integrity": "sha512-vjs6KwnCK++kIXT+eI63BGpJHfHNVJcUCr3RnvJsccT3vbJnZV5IhHR2puEkoOkIbDdp0Gqi1wEnv3hEd3WsxQ==",
3173 + "version": "2.0.22",
3174 + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.0.22.tgz",
3175 + "integrity": "sha512-dNTAAtEOuMiz7N1s5tKpypnVVCtawxVSF5BukD0ELcYSw+DSbrSlYYSw8GuwvurodCeYFSHsmslE+c2sYDNoiA==",
3176 "dev": true,
3177 "dependencies": {
3188 - "@volar/language-core": "~2.3.0-alpha.15",
3178 + "@volar/language-core": "~2.3.1",
3179 "@vue/compiler-dom": "^3.4.0",
3180 "@vue/shared": "^3.4.0",
3181 "computeds": "^0.0.1",
3182 "minimatch": "^9.0.3",
3183 + "muggle-string": "^0.4.1",
3184 "path-browserify": "^1.0.1",
3185 "vue-template-compiler": "^2.7.14"
3186 },
@@ -3203,49 +3194,49 @@
3194 }
3195 },
3196 "node_modules/@vue/reactivity": {
3206 - "version": "3.4.29",
3207 - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.29.tgz",
3208 - "integrity": "sha512-w8+KV+mb1a8ornnGQitnMdLfE0kXmteaxLdccm2XwdFxXst4q/Z7SEboCV5SqJNpZbKFeaRBBJBhW24aJyGINg==",
3197 + "version": "3.4.30",
3198 + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.30.tgz",
3199 + "integrity": "sha512-bVJurnCe3LS0JII8PPoAA63Zd2MBzcKrEzwdQl92eHCcxtIbxD2fhNwJpa+KkM3Y/A4T5FUnmdhgKwOf6BfbcA==",
3200 "dependencies": {
3210 - "@vue/shared": "3.4.29"
3201 + "@vue/shared": "3.4.30"
3202 }
3203 },
3204 "node_modules/@vue/runtime-core": {
3214 - "version": "3.4.29",
3215 - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.29.tgz",
3216 - "integrity": "sha512-s8fmX3YVR/Rk5ig0ic0NuzTNjK2M7iLuVSZyMmCzN/+Mjuqqif1JasCtEtmtoJWF32pAtUjyuT2ljNKNLeOmnQ==",
3205 + "version": "3.4.30",
3206 + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.30.tgz",
3207 + "integrity": "sha512-qaFEbnNpGz+tlnkaualomogzN8vBLkgzK55uuWjYXbYn039eOBZrWxyXWq/7qh9Bz2FPifZqGjVDl/FXiq9L2g==",
3208 "dependencies": {
3218 - "@vue/reactivity": "3.4.29",
3219 - "@vue/shared": "3.4.29"
3209 + "@vue/reactivity": "3.4.30",
3210 + "@vue/shared": "3.4.30"
3211 }
3212 },
3213 "node_modules/@vue/runtime-dom": {
3223 - "version": "3.4.29",
3224 - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.29.tgz",
3225 - "integrity": "sha512-gI10atCrtOLf/2MPPMM+dpz3NGulo9ZZR9d1dWo4fYvm+xkfvRrw1ZmJ7mkWtiJVXSsdmPbcK1p5dZzOCKDN0g==",
3214 + "version": "3.4.30",
3215 + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.30.tgz",
3216 + "integrity": "sha512-tV6B4YiZRj5QsaJgw2THCy5C1H+2UeywO9tqgWEc21tn85qHEERndHN/CxlyXvSBFrpmlexCIdnqPuR9RM9thw==",
3217 "dependencies": {
3227 - "@vue/reactivity": "3.4.29",
3228 - "@vue/runtime-core": "3.4.29",
3229 - "@vue/shared": "3.4.29",
3218 + "@vue/reactivity": "3.4.30",
3219 + "@vue/runtime-core": "3.4.30",
3220 + "@vue/shared": "3.4.30",
3221 "csstype": "^3.1.3"
3222 }
3223 },
3224 "node_modules/@vue/server-renderer": {
3234 - "version": "3.4.29",
3235 - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.29.tgz",
3236 - "integrity": "sha512-HMLCmPI2j/k8PVkSBysrA2RxcxC5DgBiCdj7n7H2QtR8bQQPqKAe8qoaxLcInzouBmzwJ+J0x20ygN/B5mYBng==",
3225 + "version": "3.4.30",
3226 + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.30.tgz",
3227 + "integrity": "sha512-TBD3eqR1DeDc0cMrXS/vEs/PWzq1uXxnvjoqQuDGFIEHFIwuDTX/KWAQKIBjyMWLFHEeTDGYVsYci85z2UbTDg==",
3228 "dependencies": {
3238 - "@vue/compiler-ssr": "3.4.29",
3239 - "@vue/shared": "3.4.29"
3229 + "@vue/compiler-ssr": "3.4.30",
3230 + "@vue/shared": "3.4.30"
3231 },
3232 "peerDependencies": {
3242 - "vue": "3.4.29"
3233 + "vue": "3.4.30"
3234 }
3235 },
3236 "node_modules/@vue/shared": {
3246 - "version": "3.4.29",
3247 - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.29.tgz",
3248 - "integrity": "sha512-hQ2gAQcBO/CDpC82DCrinJNgOHI2v+FA7BDW4lMSPeBpQ7sRe2OLHWe5cph1s7D8DUQAwRt18dBDfJJ220APEA=="
3237 + "version": "3.4.30",
3238 + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.30.tgz",
3239 + "integrity": "sha512-CLg+f8RQCHQnKvuHY9adMsMaQOcqclh6Z5V9TaoMgy0ut0tz848joZ7/CYFFyF/yZ5i2yaw7Fn498C+CNZVHIg=="
3240 },
3241 "node_modules/@vue/test-utils": {
3242 "version": "2.4.6",
@@ -4580,9 +4571,9 @@
4571 "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
4572 },
4573 "node_modules/cypress": {
4583 - "version": "13.11.0",
4584 - "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.11.0.tgz",
4585 - "integrity": "sha512-NXXogbAxVlVje4XHX+Cx5eMFZv4Dho/2rIcdBHg9CNPFUGZdM4cRdgIgM7USmNYsC12XY0bZENEQ+KBk72fl+A==",
4574 + "version": "13.12.0",
4575 + "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.12.0.tgz",
4576 + "integrity": "sha512-udzS2JilmI9ApO/UuqurEwOvThclin5ntz7K0BtnHBs+tg2Bl9QShLISXpSEMDv/u8b6mqdoAdyKeZiSqKWL8g==",
4577 "dev": true,
4578 "hasInstallScript": true,
4579 "dependencies": {
@@ -9371,12 +9362,15 @@
9362 }
9363 },
9364 "node_modules/secure-ls": {
9374 - "version": "1.2.6",
9375 - "resolved": "https://registry.npmjs.org/secure-ls/-/secure-ls-1.2.6.tgz",
9376 - "integrity": "sha512-g8vUSKl6elSfyAUHodybnNkuZW+mUYEOWj4SZIDg+xoQ1dq5ddktBoOFrtxQBUl88ZyAJOtGWQ1PRaOxkTAuZQ==",
9365 + "version": "2.0.0",
9366 + "resolved": "https://registry.npmjs.org/secure-ls/-/secure-ls-2.0.0.tgz",
9367 + "integrity": "sha512-Wgtnw0QSm0v7gVKv11nOoeyGS65EThGXnBB7jfd4IhZd2eq3B4AMPcXAL5qJ1h55+Qolun7TONTwX7H5m6e2pQ==",
9368 "dependencies": {
9378 - "crypto-js": "^3.1.6",
9379 - "lz-string": "^1.4.4"
9369 + "crypto-js": "^4.2.0",
9370 + "lz-string": "^1.5.0"
9371 + },
9372 + "engines": {
9373 + "node": ">=8.0"
9374 }
9375 },
9376 "node_modules/seemly": {
@@ -9447,11 +9441,11 @@
9441 }
9442 },
9443 "node_modules/shiki": {
9450 - "version": "1.7.0",
9451 - "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.7.0.tgz",
9452 - "integrity": "sha512-H5pMn4JA7ayx8H0qOz1k2qANq6mZVCMl1gKLK6kWIrv1s2Ial4EmD4s4jE8QB5Dw03d/oCQUxc24sotuyR5byA==",
9444 + "version": "1.9.0",
9445 + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.9.0.tgz",
9446 + "integrity": "sha512-i6//Lqgn7+7nZA0qVjoYH0085YdNk4MC+tJV4bo+HgjgRMJ0JmkLZzFAuvVioJqLkcGDK5GAMpghZEZkCnwxpQ==",
9447 "dependencies": {
9454 - "@shikijs/core": "1.7.0"
9448 + "@shikijs/core": "1.9.0"
9449 }
9450 },
9451 "node_modules/side-channel": {
@@ -10042,9 +10036,9 @@
10036 "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ=="
10037 },
10038 "node_modules/tailwind-config-viewer": {
10045 - "version": "2.0.3",
10046 - "resolved": "https://registry.npmjs.org/tailwind-config-viewer/-/tailwind-config-viewer-2.0.3.tgz",
10047 - "integrity": "sha512-UxeBz6Q0XLCmVcJ2IiYqM1vuxjIkGTf0idxpwb7LkCb/FEebEmUGqwE7XSGPrD8uL7bmCfWskyPGiwNtzMmrJw==",
10039 + "version": "2.0.4",
10040 + "resolved": "https://registry.npmjs.org/tailwind-config-viewer/-/tailwind-config-viewer-2.0.4.tgz",
10041 + "integrity": "sha512-icvcmdMmt9dphvas8wL40qttrHwAnW3QEN4ExJ2zICjwRsPj7gowd1cOceaWG3IfTuM/cTNGQcx+bsjMtmV+cw==",
10042 "dev": true,
10043 "dependencies": {
10044 "@koa/router": "^12.0.1",
@@ -10815,14 +10809,14 @@
10809 }
10810 },
10811 "node_modules/vite-plugin-vue-devtools": {
10818 - "version": "7.3.1",
10819 - "resolved": "https://registry.npmjs.org/vite-plugin-vue-devtools/-/vite-plugin-vue-devtools-7.3.1.tgz",
10820 - "integrity": "sha512-KuksceHlb5QZtb5gRB4wuRiquZRX74//i0X5jzvy5QzY11qwK44goyVrhPupZbsNfqwmZWNi3CQAe0RhLBUylg==",
10812 + "version": "7.3.4",
10813 + "resolved": "https://registry.npmjs.org/vite-plugin-vue-devtools/-/vite-plugin-vue-devtools-7.3.4.tgz",
10814 + "integrity": "sha512-5WKGIFldO9/E/J6d+x286ENieFUsexcg8Qgh/js3rEJtzipHzxiD47xMJVSBhl14n1E4jABIMuwmn1FYtpwm3w==",
10815 "dev": true,
10816 "dependencies": {
10823 - "@vue/devtools-core": "^7.3.1",
10824 - "@vue/devtools-kit": "^7.3.1",
10825 - "@vue/devtools-shared": "^7.3.1",
10817 + "@vue/devtools-core": "^7.3.4",
10818 + "@vue/devtools-kit": "^7.3.4",
10819 + "@vue/devtools-shared": "^7.3.4",
10820 "execa": "^8.0.1",
10821 "sirv": "^2.0.4",
10822 "vite-plugin-inspect": "^0.8.4",
@@ -11218,15 +11212,15 @@
11212 "dev": true
11213 },
11214 "node_modules/vue": {
11221 - "version": "3.4.29",
11222 - "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.29.tgz",
11223 - "integrity": "sha512-8QUYfRcYzNlYuzKPfge1UWC6nF9ym0lx7mpGVPJYNhddxEf3DD0+kU07NTL0sXuiT2HuJuKr/iEO8WvXvT0RSQ==",
11215 + "version": "3.4.30",
11216 + "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.30.tgz",
11217 + "integrity": "sha512-NcxtKCwkdf1zPsr7Y8+QlDBCGqxvjLXF2EX+yi76rV5rrz90Y6gK1cq0olIhdWGgrlhs9ElHuhi9t3+W5sG5Xw==",
11218 "dependencies": {
11225 - "@vue/compiler-dom": "3.4.29",
11226 - "@vue/compiler-sfc": "3.4.29",
11227 - "@vue/runtime-dom": "3.4.29",
11228 - "@vue/server-renderer": "3.4.29",
11229 - "@vue/shared": "3.4.29"
11219 + "@vue/compiler-dom": "3.4.30",
11220 + "@vue/compiler-sfc": "3.4.30",
11221 + "@vue/runtime-dom": "3.4.30",
11222 + "@vue/server-renderer": "3.4.30",
11223 + "@vue/shared": "3.4.30"
11224 },
11225 "peerDependencies": {
11226 "typescript": "*"
@@ -11345,9 +11339,9 @@
11339 }
11340 },
11341 "node_modules/vue-router": {
11348 - "version": "4.3.3",
11349 - "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.3.3.tgz",
11350 - "integrity": "sha512-8Q+u+WP4N2SXY38FDcF2H1dUEbYVHVPtPCPZj/GTZx8RCbiB8AtJP9+YIxn4Vs0svMTNQcLIzka4GH7Utkx9xQ==",
11342 + "version": "4.4.0",
11343 + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.4.0.tgz",
11344 + "integrity": "sha512-HB+t2p611aIZraV2aPSRNXf0Z/oLZFrlygJm+sZbdJaW6lcFqEDQwnzUBXn+DApw+/QzDU/I9TeWx9izEjTmsA==",
11345 "dependencies": {
11346 "@vue/devtools-api": "^6.5.1"
11347 },
@@ -11377,13 +11371,13 @@
11371 }
11372 },
11373 "node_modules/vue-tsc": {
11380 - "version": "2.0.21",
11381 - "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.0.21.tgz",
11382 - "integrity": "sha512-E6x1p1HaHES6Doy8pqtm7kQern79zRtIewkf9fiv7Y43Zo4AFDS5hKi+iHi2RwEhqRmuiwliB1LCEFEGwvxQnw==",
11374 + "version": "2.0.22",
11375 + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.0.22.tgz",
11376 + "integrity": "sha512-lMBIwPBO0sxCcmvu45yt1b035AaQ8/XSXQDk8m75y4j0jSXY/y/XzfEtssQ9JMS47lDaR10O3/926oCs8OeGUw==",
11377 "dev": true,
11378 "dependencies": {
11385 - "@volar/typescript": "~2.3.0-alpha.15",
11386 - "@vue/language-core": "2.0.21",
11379 + "@volar/typescript": "~2.3.1",
11380 + "@vue/language-core": "2.0.22",
11381 "semver": "^7.5.4"
11382 },
11383 "bin": {
frontend/package.json
+10 -10
@@ -36,7 +36,7 @@
36 "@fontsource/lexend": "^5.0.20",
37 "@fontsource/public-sans": "^5.0.18",
38 "@popperjs/core": "^2.11.8",
39 - "@shikijs/markdown-it": "^1.7.0",
39 + "@shikijs/markdown-it": "^1.9.0",
40 "@vueuse/components": "^10.11.0",
41 "@vueuse/core": "^10.11.0",
42 "apexcharts": "^3.49.1",
@@ -56,14 +56,14 @@
56 "password-validator": "^5.3.0",
57 "pinia": "^2.1.7",
58 "pinia-plugin-persistedstate": "^3.2.1",
59 - "secure-ls": "^1.2.6",
60 - "shiki": "^1.7.0",
59 + "secure-ls": "^2.0.0",
60 + "shiki": "^1.9.0",
61 "validator": "^13.12.0",
62 - "vue": "^3.4.29",
62 + "vue": "^3.4.30",
63 "vue-advanced-cropper": "^2.8.9",
64 "vue-highlight-words": "^3.0.1",
65 "vue-i18n": "^9.13.1",
66 - "vue-router": "^4.3.3",
66 + "vue-router": "^4.4.0",
67 "vue-sjv": "^0.0.6",
68 "vue3-apexcharts": "^1.5.3",
69 "vue3-marquee": "^4.2.0",
@@ -79,7 +79,7 @@
79 "@types/inquirer": "^9.0.7",
80 "@types/jsdom": "^21.1.7",
81 "@types/lodash": "^4.17.5",
82 - "@types/node": "^20.14.5",
82 + "@types/node": "^20.14.8",
83 "@types/validator": "^13.12.0",
84 "@vitejs/plugin-vue": "^5.0.5",
85 "@vitejs/plugin-vue-jsx": "^4.0.0",
@@ -88,7 +88,7 @@
88 "@vue/test-utils": "^2.4.6",
89 "@vue/tsconfig": "^0.5.1",
90 "autoprefixer": "^10.4.19",
91 - "cypress": "^13.11.0",
91 + "cypress": "^13.12.0",
92 "eslint": "^9.5.0",
93 "eslint-plugin-cypress": "^3.3.0",
94 "eslint-plugin-vue": "^9.26.0",
@@ -103,7 +103,7 @@
103 "prettier": "^3.3.2",
104 "sass": "^1.77.6",
105 "start-server-and-test": "^2.0.4",
106 - "tailwind-config-viewer": "^2.0.3",
106 + "tailwind-config-viewer": "^2.0.4",
107 "tailwindcss": "^3.4.4",
108 "taze": "^0.13.8",
109 "type-fest": "^4.20.1",
@@ -111,10 +111,10 @@
111 "vite": "^5.3.1",
112 "vite-bundle-analyzer": "^0.10.3",
113 "vite-bundle-visualizer": "^1.2.1",
114 - "vite-plugin-vue-devtools": "^7.3.1",
114 + "vite-plugin-vue-devtools": "^7.3.4",
115 "vite-svg-loader": "^5.1.0",
116 "vitest": "^1.6.0",
117 - "vue-tsc": "^2.0.21"
117 + "vue-tsc": "^2.0.22"
118 },
119 "optionalDependencies": {
120 "@rollup/rollup-linux-x64-gnu": "^4.18.0"
frontend/src/api/agents.ts
+4 -2
@@ -13,6 +13,8 @@ export interface AgentPayload {
13 velociraptor_id: string
14 }
15
16 +export type VulnerabilitySeverityType = "Low" | "Medium" | "High" | "Critical"
17 +
18 export default {
19 getAgents(agentId?: string) {
20 return HttpClient.get<FlaskBaseResponse & { agents: Agent[] }>(`/agents${agentId ? "/" + agentId : ""}`)
@@ -29,9 +31,9 @@ export default {
31 syncAgents() {
32 return HttpClient.post<FlaskBaseResponse>(`/agents/sync`)
33 },
32 - agentVulnerabilities(agentId: string) {
34 + agentVulnerabilities(agentId: string, severity: VulnerabilitySeverityType) {
35 return HttpClient.get<FlaskBaseResponse & { vulnerabilities: AgentVulnerabilities[] }>(
34 - `/agents/${agentId}/vulnerabilities`
36 + `/agents/${agentId}/vulnerabilities/${severity}`
37 )
38 },
39 getSocCases(agentId: string | number, signal?: AbortSignal) {
frontend/src/components/agents/vulnerabilities/VulnerabilitiesGrid.vue
+30 -7
@@ -1,7 +1,12 @@
1 <template>
2 - <n-spin class="vulnerabilities-section" :show="loading">
2 + <n-spin class="vulnerabilities-section" content-class="min-h-48" :show="loading">
3 + <div class="toolbar">
4 + <n-form-item label="Severity" label-placement="left" size="small">
5 + <n-select v-model:value="severity" :options="severityOptions" class="max-w-48" />
6 + </n-form-item>
7 + </div>
8 <div class="group">
4 - <VulnerabilityCard :vulnerability="item" v-for="item of vulnerabilities" :key="item.id" />
9 + <VulnerabilityCard :vulnerability="item" v-for="item of vulnerabilities" :key="item.id" hide-tooltip />
10 </div>
11 <n-empty
12 description="No vulnerabilities detected"
@@ -12,12 +17,13 @@
17 </template>
18
19 <script setup lang="ts">
15 -import { ref, onBeforeMount, toRefs } from "vue"
20 +import { ref, onBeforeMount, toRefs, watch, computed } from "vue"
21 import Api from "@/api"
22 import { type Agent, type AgentVulnerabilities } from "@/types/agents.d"
23 import VulnerabilityCard from "./VulnerabilityCard.vue"
24 import { nanoid } from "nanoid"
20 -import { useMessage, NSpin, NEmpty } from "naive-ui"
25 +import { useMessage, NSpin, NEmpty, NFormItem, NSelect } from "naive-ui"
26 +import type { VulnerabilitySeverityType } from "@/api/agents"
27
28 const props = defineProps<{
29 agent: Agent
@@ -26,16 +32,33 @@ const { agent } = toRefs(props)
32
33 const message = useMessage()
34 const loading = ref(false)
29 -const vulnerabilities = ref<AgentVulnerabilities[]>([])
35 +const severity = ref<VulnerabilitySeverityType>("Critical")
36 +const vulnerabilitiesCache = ref<{ [key in VulnerabilitySeverityType | string]: AgentVulnerabilities[] }>({})
37 +const vulnerabilities = computed<AgentVulnerabilities[]>(() => vulnerabilitiesCache.value[severity.value] || [])
38 +
39 +const severityOptions: { label: string; value: VulnerabilitySeverityType }[] = [
40 + { label: "Critical", value: "Critical" },
41 + { label: "High", value: "High" },
42 + { label: "Medium", value: "Medium" },
43 + { label: "Low", value: "Low" }
44 +]
45 +
46 +watch(severity, () => {
47 + if (agent?.value?.agent_id) getVulnerabilities(agent.value.agent_id)
48 +})
49
50 function getVulnerabilities(id: string) {
51 + if (severity.value in vulnerabilitiesCache.value) {
52 + return
53 + }
54 +
55 loading.value = true
56
57 Api.agents
35 - .agentVulnerabilities(id)
58 + .agentVulnerabilities(id, severity.value)
59 .then(res => {
60 if (res.data.success) {
38 - vulnerabilities.value = (res.data.vulnerabilities || []).map(o => {
61 + vulnerabilitiesCache.value[severity.value] = (res.data.vulnerabilities || []).map(o => {
62 o.id = nanoid()
63 return o
64 })
frontend/src/components/agents/vulnerabilities/VulnerabilityCard.vue
+64 -40
@@ -1,12 +1,18 @@
1 <template>
2 <div class="vulnerability-card" :class="`severity-${vulnerability.severity}`" @click="showDialog = true">
3 - <div class="severity" :severity="vulnerability.severity">{{ vulnerability.severity }}</div>
3 + <div class="severity" :severity="vulnerability.severity">
4 + {{ vulnerability.severity }}
5 + </div>
6 <div class="wrapper">
7 <div class="title">
6 - <span>{{ vulnerability.title }}</span>
8 + <span>{{ title }}</span>
9 </div>
10 <div class="property">
9 - <n-tooltip placement="top-start">
11 + <div v-if="hideTooltip">
12 + <Icon :name="ClockIcon"></Icon>
13 + <span>{{ detectionTime }}</span>
14 + </div>
15 + <n-tooltip placement="top-start" v-else>
16 <span>Detection time</span>
17 <template #trigger>
18 <div>
@@ -17,7 +23,11 @@
23 </n-tooltip>
24 </div>
25 <div class="property">
20 - <n-tooltip placement="top-start">
26 + <div v-if="hideTooltip">
27 + <Icon :name="CounterIcon"></Icon>
28 + <span>{{ vulnerability.cve }}</span>
29 + </div>
30 + <n-tooltip placement="top-start" v-else>
31 <span>{{ `CVSS2: ${vulnerability.cvss2_score} - CVSS3: ${vulnerability.cvss3_score}` }}</span>
32 <template #trigger>
33 <div>
@@ -28,7 +38,11 @@
38 </n-tooltip>
39 </div>
40 <div class="property">
31 - <n-tooltip placement="top-start">
41 + <div v-if="hideTooltip">
42 + <Icon :name="VulnerabilityIcon"></Icon>
43 + <span>{{ vulnerability.name }}</span>
44 + </div>
45 + <n-tooltip placement="top-start" v-else>
46 <span>{{ `Version: ${vulnerability.version}` }}</span>
47 <template #trigger>
48 <div>
@@ -39,36 +53,42 @@
53 </n-tooltip>
54 </div>
55 </div>
42 - </div>
56
44 - <n-modal
45 - preset="card"
46 - class="vulnerability-dialog"
47 - :title="vulnerability.title"
48 - v-model:show="showDialog"
49 - content-class="!p-0"
50 - style="width: 90vw; max-width: 1000px"
51 - >
52 - <n-tabs type="line" animated :tabs-padding="24">
53 - <n-tab-pane name="Details" tab="Details" display-directive="show">
54 - <div class="grid gap-2 grid-auto-flow-200 p-7 pt-4" v-if="vulnerabilitySanitized">
55 - <KVCard v-for="item of vulnerabilitySanitized" :key="item.label">
56 - <template #key>{{ item.label }}</template>
57 - <template #value>{{ item.value ?? "-" }}</template>
58 - </KVCard>
59 - </div>
60 - </n-tab-pane>
61 - <n-tab-pane name="External references" tab="External references" display-directive="show">
62 - <div class="p-7 pt-2">
63 - <ul>
64 - <li v-for="ref of vulnerability.external_references" :key="ref">
65 - <a :href="ref" target="_blank">{{ ref }}</a>
66 - </li>
67 - </ul>
68 - </div>
69 - </n-tab-pane>
70 - </n-tabs>
71 - </n-modal>
57 + <n-modal
58 + preset="card"
59 + class="vulnerability-dialog"
60 + :title="extract"
61 + v-model:show="showDialog"
62 + content-class="!p-0"
63 + style="width: 90vw; max-width: 1000px"
64 + >
65 + <n-tabs type="line" animated :tabs-padding="24">
66 + <n-tab-pane name="Details" tab="Details" display-directive="show">
67 + <div class="p-7 pt-4 pb-0">
68 + <KVCard>
69 + <template #key>title</template>
70 + <template #value>{{ vulnerability.title ?? "-" }}</template>
71 + </KVCard>
72 + </div>
73 + <div class="grid gap-2 grid-auto-flow-200 p-7 pt-2" v-if="vulnerabilitySanitized">
74 + <KVCard v-for="item of vulnerabilitySanitized" :key="item.label">
75 + <template #key>{{ item.label }}</template>
76 + <template #value>{{ item.value ?? "-" }}</template>
77 + </KVCard>
78 + </div>
79 + </n-tab-pane>
80 + <n-tab-pane name="External references" tab="External references" display-directive="show">
81 + <div class="p-7 pt-2">
82 + <ul>
83 + <li v-for="ref of vulnerability.external_references" :key="ref">
84 + <a :href="ref" target="_blank">{{ ref }}</a>
85 + </li>
86 + </ul>
87 + </div>
88 + </n-tab-pane>
89 + </n-tabs>
90 + </n-modal>
91 + </div>
92 </template>
93
94 <script setup lang="ts">
@@ -80,18 +100,22 @@ import { NModal, NTooltip, NTabs, NTabPane } from "naive-ui"
100 import Icon from "@/components/common/Icon.vue"
101 import KVCard from "@/components/common/KVCard.vue"
102 import { useSettingsStore } from "@/stores/settings"
83 -
84 -const ClockIcon = "carbon:time"
85 -const CounterIcon = "mdi:counter"
86 -const VulnerabilityIcon = "bi:radioactive"
103 +import _truncate from "lodash/truncate"
104
105 const props = defineProps<{
106 vulnerability: AgentVulnerabilities
107 + hideTooltip?: boolean
108 }>()
91 -const { vulnerability } = toRefs(props)
109 +const { vulnerability, hideTooltip } = toRefs(props)
110
111 +const ClockIcon = "carbon:time"
112 +const CounterIcon = "mdi:counter"
113 +const VulnerabilityIcon = "bi:radioactive"
114 const dFormats = useSettingsStore().dateFormat
115
116 +const title = computed(() => _truncate(vulnerability.value.title, { length: 20 }))
117 +const extract = computed(() => _truncate(vulnerability.value.title, { length: 50 }))
118 +
119 const vulnerabilitySanitized = computed(() => {
120 const newObj = []
121 const obj = cloneDeep(vulnerability.value)
@@ -105,7 +129,7 @@ const vulnerabilitySanitized = computed(() => {
129 }
130 }
131
108 - if (!["external_references", "id"].includes(k))
132 + if (!["external_references", "id", "title"].includes(k))
133 newObj.push({
134 label: k,
135 // @ts-expect-error k is ok
frontend/src/components/indices/Marquee.vue
+1 -1
@@ -4,7 +4,7 @@
4 <n-spin :show="loading">
5 <Vue3Marquee
6 class="marquee-wrap"
7 - :duration="200"
7 + :duration="(list?.length || 0) * 1"
8 :pauseOnHover="true"
9 :clone="false"
10 :gradient="true"
frontend/src/design-tokens.json
+2
@@ -106,6 +106,7 @@
106 "divider005": "rgba(0, 0, 0, 0.05)",
107 "divider010": "rgba(0, 0, 0, 0.1)",
108 "divider020": "rgba(0, 0, 0, 0.2)",
109 + "divider030": "rgba(0, 0, 0, 0.3)",
110 "hover005": "rgba(0, 0, 0, 0.05)",
111 "hover010": "rgba(0, 0, 0, 0.1)",
112 "hover050": "rgba(0, 0, 0, 0.5)"
@@ -157,6 +158,7 @@
158 "divider005": "rgba(255, 255, 255, 0.05)",
159 "divider010": "rgba(255, 255, 255, 0.1)",
160 "divider020": "rgba(255, 255, 255, 0.2)",
161 + "divider030": "rgba(255, 255, 255, 0.3)",
162 "hover005": "rgba(255, 255, 255, 0.05)",
163 "hover010": "rgba(255, 255, 255, 0.1)",
164 "hover050": "rgba(255, 255, 255, 0.5)"
frontend/src/layouts/common/Toolbar/PinnedPages.vue
+47 -61
@@ -1,10 +1,10 @@
1 <template>
2 - <div class="flex pinned-pages items-end">
2 + <div class="flex pinned-pages items-center gap-5">
3 <TransitionGroup name="anim" tag="div" class="latest-list flex items-center gap-4 overflow-hidden">
4 <n-tag
5 round
6 :bordered="false"
7 - closable
7 + :closable="false"
8 v-for="page of latestSanitized"
9 :key="page.name"
10 @close="removeLatestPage(page.name)"
@@ -20,24 +20,32 @@
20 </n-tag>
21 </TransitionGroup>
22
23 - <div class="divider" v-if="latestSanitized.length && pinned.length"></div>
24 -
25 - <TransitionGroup name="anim" tag="div" class="pinned-list flex items-center gap-4 overflow-hidden">
26 - <n-tag
27 - round
28 - :bordered="false"
29 - closable
30 - v-for="page of pinned"
31 - :key="page.name"
32 - @close="removePinnedPage(page.name)"
33 - >
34 - <div class="page-name" @click="gotoPage(page.name)" :title="page.title">
35 - {{ page.title }}
36 - </div>
37 - </n-tag>
38 - </TransitionGroup>
39 -
40 - <div class="bar"></div>
23 + <Transition name="anim" tag="div" class="flex items-center">
24 + <div v-if="pinned.length" class="flex items-center">
25 + <n-popover :show-arrow="false" placement="bottom-end" trigger="hover">
26 + <template #trigger>
27 + <n-button size="small" class="!h-8">
28 + <span class="flex items-center gap-3">
29 + Shortcuts
30 + <n-badge :value="pinned.length" :color="style['--divider-030-color']" />
31 + </span>
32 + </n-button>
33 + </template>
34 + <div class="flex flex-col gap-3 py-2 pr-1">
35 + <div class="flex gap-2 items-center" v-for="page of pinned" :key="page.name">
36 + <n-button size="small" text @click="removePinnedPage(page.name)">
37 + <template #icon>
38 + <Icon :size="20" :name="CloseIcon" class="opacity-55"></Icon>
39 + </template>
40 + </n-button>
41 + <n-button size="small" text @click="gotoPage(page.name)" :title="page.title">
42 + {{ page.title }}
43 + </n-button>
44 + </div>
45 + </div>
46 + </n-popover>
47 + </div>
48 + </Transition>
49 </div>
50 </template>
51
@@ -45,11 +53,12 @@
53 import { useRouter, type RouteLocationNormalized, type RouteRecordName } from "vue-router"
54 import { type RemovableRef, useStorage } from "@vueuse/core"
55 import { computed, type ComputedRef } from "vue"
48 -import { NTag } from "naive-ui"
56 +import { NTag, NPopover, NButton, NBadge } from "naive-ui"
57 import _takeRight from "lodash/takeRight"
58 import _split from "lodash/split"
59 import _uniqBy from "lodash/uniqBy"
60 import Icon from "@/components/common/Icon.vue"
61 +import { useThemeStore } from "@/stores/theme"
62
63 interface Page {
64 name: RouteRecordName | string
@@ -58,7 +67,11 @@ interface Page {
67 }
68
69 const PinnedIcon = "tabler:pinned"
70 +const CloseIcon = "carbon:close"
71 const router = useRouter()
72 +const themeStore = useThemeStore()
73 +
74 +const style = computed(() => themeStore.style)
75
76 const removeLatestPage = (pageName: RouteRecordName | string) => {
77 latest.value = latest.value.filter(page => page.name !== pageName)
@@ -139,45 +152,23 @@ router.afterEach(route => {
152 transition: all 0.3s;
153 }
154
142 - &:hover {
143 - background-color: var(--bg-sidebar);
144 -
145 - &.n-tag--round {
146 - padding: 0 calc(var(--n-height) / 3.6) 0 calc(var(--n-height) / 3.6);
147 - }
148 - .n-tag__close {
149 - margin-left: 5px;
150 - overflow: initial;
151 - width: 14px;
155 + &.n-tag--closable {
156 + &:hover {
157 + background-color: var(--bg-sidebar);
158 +
159 + &.n-tag--round {
160 + padding: 0 calc(var(--n-height) / 3.6) 0 calc(var(--n-height) / 3.6);
161 + }
162 + .n-tag__close {
163 + margin-left: 5px;
164 + overflow: initial;
165 + width: 14px;
166 + }
167 }
168 }
169 }
170 }
171
157 - .bar {
158 - background-color: var(--bg-sidebar);
159 - position: absolute;
160 - bottom: 0px;
161 - border-radius: 6px;
162 - left: 0;
163 - width: 100%;
164 - height: 4px;
165 - }
166 -
167 - .divider {
168 - height: 8px;
169 - width: 8px;
170 - min-width: 8px;
171 - position: relative;
172 - top: 10px;
173 - z-index: 1;
174 - border-radius: 50%;
175 - border: 2px solid var(--bg-body);
176 - opacity: 0.9;
177 - background-color: var(--primary-color);
178 - margin: 0 8px;
179 - }
180 -
172 .page-name {
173 cursor: pointer;
174 margin-left: 2px;
@@ -190,6 +181,7 @@ router.afterEach(route => {
181 text-decoration-color: var(--primary-color);
182 }
183 }
184 +
185 .icon-box {
186 cursor: pointer;
187 transition: color 0.3s;
@@ -200,12 +192,6 @@ router.afterEach(route => {
192 }
193 }
194
203 - .pinned-list {
204 - .page-name {
205 - color: var(--primary-color);
206 - }
207 - }
208 -
195 .anim-move,
196 .anim-enter-active,
197 .anim-leave-active {
frontend/src/layouts/common/Toolbar/index.vue
+1 -1
@@ -102,7 +102,7 @@ onMounted(() => {
102 gap: 14px;
103 }
104
105 - @media (max-width: 1100px) {
105 + @media (max-width: 900px) {
106 .pinned-pages {
107 display: none;
108 }
frontend/src/stores/theme.ts
+3 -2
@@ -258,7 +258,7 @@ export const useThemeStore = defineStore("theme", {
258 return _pick(state.colors[state.themeName], pick)
259 },
260 dividerColors(state): { [key: string]: string } {
261 - const pick = ["divider005", "divider010", "divider020"]
261 + const pick = ["divider005", "divider010", "divider020", "divider030"]
262 return _pick(state.colors[state.themeName], pick)
263 },
264 hoverColors(state): { [key: string]: string } {
@@ -332,7 +332,7 @@ export const useThemeStore = defineStore("theme", {
332 const borderRadius = state.borderRadius.base
333 const borderRadiusSmall = state.borderRadius.small
334
335 - const { divider005, divider010, divider020 } = this.dividerColors
335 + const { divider005, divider010, divider020, divider030 } = this.dividerColors
336 const { hover005, hover010, hover050 } = this.hoverColors
337 const { primary005, primary010, primary015, primary020, primary030, primary040, primary050, primary060 } =
338 this.primaryColors
@@ -416,6 +416,7 @@ export const useThemeStore = defineStore("theme", {
416 "--divider-005-color": `${divider005}`,
417 "--divider-010-color": `${divider010}`,
418 "--divider-020-color": `${divider020}`,
419 + "--divider-030-color": `${divider030}`,
420
421 "--success-color": `${successColor}`,
422 "--error-color": `${errorColor}`,