@cryptotaxi247 / CoPilot / commits / b3290324

Bitdefender integration (#289)

* feat: Add BitDefender DB integration to SOCFortress * feat: Add BitDefender integration to SOCFortress * feat: Add BitDefender integration to SOCFortress - graylog templates * feat: continued BitDefender integration to SOCFortress * feat: Update BitDefender integration to use TCP protocol for syslog * push test build * refactor: Update BitDefender integration to use correct protocol name for syslog * refactor: Update BitDefender README * update dependencies * update gitignore * feat(fe): add BitDefender deploy * precommit fixes --------- Co-authored-by: Davide Di Modica <webmaster.ddm@gmail.com>

taylor_socfortress committed Sep 11, 2024 at 11:12 UTC b329032456f0b50a9b154da79d8cdaa649ed7277
21 files changed +4753 -200
.gitignore
+2
@@ -52,6 +52,8 @@ site/
52 firewall.spec
53
54 backend/data/api.config.yaml
55 +backend/data/SOCFORTRESS/config.json
56 +backend/data/SOCFORTRESS/SOCFORTRESS_bitdefender_docker-compose.yml
57 backend/file-store/api.config.yaml
58 backend/report.pdf
59 backend/report.html
.vscode/settings.json
+1
@@ -4,6 +4,7 @@
4 "apexchart",
5 "arcticons",
6 "artifactstring",
7 + "bitdefender",
8 "CARBONBLACK",
9 "clickoutside",
10 "cmdline",
backend/app/connectors/grafana/dashboards/Bitdefender/summary.json new
+2863
@@ -0,0 +1,2863 @@
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 + "links": [
28 + {
29 + "asDropdown": true,
30 + "icon": "external link",
31 + "includeVars": true,
32 + "keepTime": true,
33 + "tags": ["EDR"],
34 + "targetBlank": true,
35 + "title": "",
36 + "type": "dashboards"
37 + }
38 + ],
39 + "liveNow": false,
40 + "panels": [
41 + {
42 + "datasource": {
43 + "type": "grafana-opensearch-datasource",
44 + "uid": "replace_datasource_uid"
45 + },
46 + "fieldConfig": {
47 + "defaults": {
48 + "mappings": [
49 + {
50 + "options": {
51 + "match": "null",
52 + "result": {
53 + "text": "N/A"
54 + }
55 + },
56 + "type": "special"
57 + }
58 + ],
59 + "thresholds": {
60 + "mode": "absolute",
61 + "steps": [
62 + {
63 + "color": "red",
64 + "value": null
65 + }
66 + ]
67 + },
68 + "unit": "locale"
69 + },
70 + "overrides": []
71 + },
72 + "gridPos": {
73 + "h": 7,
74 + "w": 4,
75 + "x": 0,
76 + "y": 0
77 + },
78 + "id": 43,
79 + "links": [],
80 + "options": {
81 + "colorMode": "value",
82 + "graphMode": "area",
83 + "justifyMode": "auto",
84 + "orientation": "horizontal",
85 + "reduceOptions": {
86 + "calcs": ["sum"],
87 + "fields": "",
88 + "values": false
89 + },
90 + "text": {},
91 + "textMode": "auto"
92 + },
93 + "pluginVersion": "9.2.1",
94 + "targets": [
95 + {
96 + "bucketAggs": [
97 + {
98 + "field": "timestamp",
99 + "id": "2",
100 + "settings": {
101 + "interval": "auto",
102 + "min_doc_count": 0,
103 + "trimEdges": 0
104 + },
105 + "type": "date_histogram"
106 + }
107 + ],
108 + "datasource": {
109 + "type": "grafana-opensearch-datasource",
110 + "uid": "replace_datasource_uid"
111 + },
112 + "metrics": [
113 + {
114 + "field": "select field",
115 + "id": "1",
116 + "type": "count"
117 + }
118 + ],
119 + "query": "level:>=6 AND BitdefenderGZComputerFQDN:$agent_name",
120 + "refId": "A",
121 + "timeField": "timestamp"
122 + }
123 + ],
124 + "title": "ALERTS",
125 + "type": "stat"
126 + },
127 + {
128 + "datasource": {
129 + "type": "grafana-opensearch-datasource",
130 + "uid": "replace_datasource_uid"
131 + },
132 + "fieldConfig": {
133 + "defaults": {
134 + "color": {
135 + "mode": "thresholds"
136 + },
137 + "custom": {
138 + "align": "auto",
139 + "displayMode": "auto",
140 + "inspect": false
141 + },
142 + "mappings": [],
143 + "thresholds": {
144 + "mode": "absolute",
145 + "steps": [
146 + {
147 + "color": "red",
148 + "value": null
149 + }
150 + ]
151 + }
152 + },
153 + "overrides": [
154 + {
155 + "matcher": {
156 + "id": "byName",
157 + "options": "Time"
158 + },
159 + "properties": [
160 + {
161 + "id": "displayName",
162 + "value": "Time"
163 + },
164 + {
165 + "id": "unit",
166 + "value": "time: YYYY-MM-DD HH:mm:ss"
167 + },
168 + {
169 + "id": "custom.align"
170 + }
171 + ]
172 + },
173 + {
174 + "matcher": {
175 + "id": "byName",
176 + "options": "Count"
177 + },
178 + "properties": [
179 + {
180 + "id": "displayName",
181 + "value": "EVENTS"
182 + },
183 + {
184 + "id": "unit",
185 + "value": "short"
186 + },
187 + {
188 + "id": "decimals",
189 + "value": -1
190 + },
191 + {
192 + "id": "custom.displayMode",
193 + "value": "color-background"
194 + },
195 + {
196 + "id": "custom.align"
197 + },
198 + {
199 + "id": "thresholds",
200 + "value": {
201 + "mode": "absolute",
202 + "steps": [
203 + {
204 + "color": "red",
205 + "value": null
206 + }
207 + ]
208 + }
209 + }
210 + ]
211 + },
212 + {
213 + "matcher": {
214 + "id": "byName",
215 + "options": "agent_name"
216 + },
217 + "properties": [
218 + {
219 + "id": "displayName",
220 + "value": "AGENT"
221 + },
222 + {
223 + "id": "custom.displayMode",
224 + "value": "color-background"
225 + },
226 + {
227 + "id": "custom.align"
228 + },
229 + {
230 + "id": "links",
231 + "value": [
232 + {
233 + "targetBlank": true,
234 + "title": "VIEW EVENTS",
235 + "url": "https://grafana.company.local/explore?left=%5B%22now-1h%22,%22now%22,%22BITDEFENDER%22,%7B%22refId%22:%22A%22,%22query%22:%22agent_name:${__value.text}%22,%22alias%22:%22%22,%22metrics%22:%5B%7B%22id%22:%221%22,%22type%22:%22logs%22,%22settings%22:%7B%22limit%22:%22500%22%7D%7D%5D,%22bucketAggs%22:%5B%5D,%22timeField%22:%22timestamp%22%7D%5D"
236 + }
237 + ]
238 + }
239 + ]
240 + },
241 + {
242 + "matcher": {
243 + "id": "byName",
244 + "options": "Count"
245 + },
246 + "properties": [
247 + {
248 + "id": "displayName",
249 + "value": "ALERTS"
250 + },
251 + {
252 + "id": "unit",
253 + "value": "short"
254 + },
255 + {
256 + "id": "decimals",
257 + "value": 0
258 + },
259 + {
260 + "id": "custom.align"
261 + }
262 + ]
263 + }
264 + ]
265 + },
266 + "gridPos": {
267 + "h": 7,
268 + "w": 7,
269 + "x": 4,
270 + "y": 0
271 + },
272 + "id": 31,
273 + "options": {
274 + "footer": {
275 + "fields": "",
276 + "reducer": ["sum"],
277 + "show": false
278 + },
279 + "showHeader": true
280 + },
281 + "pluginVersion": "9.2.1",
282 + "targets": [
283 + {
284 + "bucketAggs": [
285 + {
286 + "fake": true,
287 + "field": "BitdefenderGZComputerFQDN",
288 + "id": "4",
289 + "settings": {
290 + "min_doc_count": 1,
291 + "order": "desc",
292 + "orderBy": "_term",
293 + "size": "0"
294 + },
295 + "type": "terms"
296 + }
297 + ],
298 + "datasource": {
299 + "type": "grafana-opensearch-datasource",
300 + "uid": "replace_datasource_uid"
301 + },
302 + "metrics": [
303 + {
304 + "field": "select field",
305 + "id": "1",
306 + "type": "count"
307 + }
308 + ],
309 + "query": "level:>=6 AND BitdefenderGZComputerFQDN:$agent_name",
310 + "refId": "A",
311 + "timeField": "timestamp"
312 + }
313 + ],
314 + "title": "ALERTS BY AGENT",
315 + "transformations": [
316 + {
317 + "id": "merge",
318 + "options": {
319 + "reducers": []
320 + }
321 + },
322 + {
323 + "id": "organize",
324 + "options": {
325 + "excludeByName": {},
326 + "indexByName": {},
327 + "renameByName": {
328 + "BitdefenderGZComputerFQDN": "AGENT"
329 + }
330 + }
331 + }
332 + ],
333 + "type": "table"
334 + },
335 + {
336 + "datasource": {
337 + "type": "grafana-opensearch-datasource",
338 + "uid": "replace_datasource_uid"
339 + },
340 + "gridPos": {
341 + "h": 14,
342 + "w": 13,
343 + "x": 11,
344 + "y": 0
345 + },
346 + "id": 47,
347 + "options": {
348 + "dedupStrategy": "signature",
349 + "enableLogDetails": true,
350 + "prettifyLogMessage": false,
351 + "showCommonLabels": false,
352 + "showLabels": false,
353 + "showTime": true,
354 + "sortOrder": "Descending",
355 + "wrapLogMessage": false
356 + },
357 + "targets": [
358 + {
359 + "alias": "",
360 + "bucketAggs": [],
361 + "datasource": {
362 + "type": "grafana-opensearch-datasource",
363 + "uid": "replace_datasource_uid"
364 + },
365 + "metrics": [
366 + {
367 + "id": "1",
368 + "settings": {
369 + "limit": "250"
370 + },
371 + "type": "logs"
372 + }
373 + ],
374 + "query": "level:>=6 AND BitdefenderGZComputerFQDN:$agent_name",
375 + "refId": "A",
376 + "timeField": "timestamp"
377 + }
378 + ],
379 + "title": "ALERTS - DETAILS",
380 + "transformations": [
381 + {
382 + "id": "filterFieldsByName",
383 + "options": {
384 + "include": {
385 + "names": [
386 + "timestamp",
387 + "level",
388 + "BitdefenderGZModule",
389 + "_id",
390 + "BitdefenderGZComputerFQDN",
391 + "name"
392 + ]
393 + }
394 + }
395 + }
396 + ],
397 + "transparent": true,
398 + "type": "logs"
399 + },
400 + {
401 + "datasource": {
402 + "type": "grafana-opensearch-datasource",
403 + "uid": "replace_datasource_uid"
404 + },
405 + "fieldConfig": {
406 + "defaults": {
407 + "color": {
408 + "mode": "thresholds"
409 + },
410 + "mappings": [
411 + {
412 + "options": {
413 + "match": "null",
414 + "result": {
415 + "text": "N/A"
416 + }
417 + },
418 + "type": "special"
419 + }
420 + ],
421 + "max": 15,
422 + "min": 0,
423 + "thresholds": {
424 + "mode": "absolute",
425 + "steps": [
426 + {
427 + "color": "#299c46",
428 + "value": null
429 + },
430 + {
431 + "color": "rgba(237, 129, 40, 0.89)",
432 + "value": 8
433 + },
434 + {
435 + "color": "#d44a3a",
436 + "value": 12
437 + }
438 + ]
439 + },
440 + "unit": "none"
441 + },
442 + "overrides": []
443 + },
444 + "gridPos": {
445 + "h": 7,
446 + "w": 4,
447 + "x": 0,
448 + "y": 7
449 + },
450 + "id": 16,
451 + "links": [],
452 + "options": {
453 + "orientation": "horizontal",
454 + "reduceOptions": {
455 + "calcs": ["max"],
456 + "fields": "",
457 + "values": false
458 + },
459 + "showThresholdLabels": false,
460 + "showThresholdMarkers": true,
461 + "text": {}
462 + },
463 + "pluginVersion": "9.2.1",
464 + "targets": [
465 + {
466 + "bucketAggs": [
467 + {
468 + "field": "timestamp",
469 + "id": "2",
470 + "settings": {
471 + "interval": "auto",
472 + "min_doc_count": 0,
473 + "trimEdges": 0
474 + },
475 + "type": "date_histogram"
476 + }
477 + ],
478 + "datasource": {
479 + "type": "grafana-opensearch-datasource",
480 + "uid": "replace_datasource_uid"
481 + },
482 + "metrics": [
483 + {
484 + "field": "level",
485 + "id": "1",
486 + "meta": {},
487 + "settings": {},
488 + "type": "max"
489 + }
490 + ],
491 + "query": "BitdefenderGZComputerFQDN:$agent_name",
492 + "refId": "A",
493 + "timeField": "timestamp"
494 + }
495 + ],
496 + "title": "MAX SEVERITY (0 - 15)",
497 + "type": "gauge"
498 + },
499 + {
500 + "columns": [],
501 + "datasource": {
502 + "type": "grafana-opensearch-datasource",
503 + "uid": "replace_datasource_uid"
504 + },
505 + "fontSize": "100%",
506 + "gridPos": {
507 + "h": 7,
508 + "w": 7,
509 + "x": 4,
510 + "y": 7
511 + },
512 + "id": 44,
513 + "showHeader": true,
514 + "sort": {
515 + "col": 0,
516 + "desc": true
517 + },
518 + "styles": [
519 + {
520 + "alias": "Time",
521 + "align": "auto",
522 + "dateFormat": "YYYY-MM-DD HH:mm:ss",
523 + "pattern": "Time",
524 + "type": "date"
525 + },
526 + {
527 + "alias": "EVENTS",
528 + "align": "auto",
529 + "colorMode": "row",
530 + "colors": ["rgba(50, 172, 45, 0.97)", "rgba(237, 129, 40, 0.89)", "#F2495C"],
531 + "dateFormat": "YYYY-MM-DD HH:mm:ss",
532 + "decimals": -1,
533 + "mappingType": 1,
534 + "pattern": "Count",
535 + "thresholds": ["0", "1"],
536 + "type": "number",
537 + "unit": "short"
538 + },
539 + {
540 + "alias": "ALERTS BY TYPE",
541 + "align": "auto",
542 + "colorMode": "row",
543 + "colors": ["#37872D", "rgba(237, 129, 40, 0.89)", "rgba(245, 54, 54, 0.9)"],
544 + "decimals": -1,
545 + "pattern": "BitdefenderGZModule",
546 + "preserveFormat": false,
547 + "sanitize": false,
548 + "thresholds": [],
549 + "type": "string",
550 + "unit": "short"
551 + }
552 + ],
553 + "targets": [
554 + {
555 + "bucketAggs": [
556 + {
557 + "fake": true,
558 + "field": "BitdefenderGZModule",
559 + "id": "4",
560 + "settings": {
561 + "min_doc_count": 1,
562 + "order": "desc",
563 + "orderBy": "_term",
564 + "size": "0"
565 + },
566 + "type": "terms"
567 + }
568 + ],
569 + "datasource": {
570 + "type": "grafana-opensearch-datasource",
571 + "uid": "replace_datasource_uid"
572 + },
573 + "metrics": [
574 + {
575 + "field": "select field",
576 + "id": "1",
577 + "type": "count"
578 + }
579 + ],
580 + "query": "level:>=6 AND BitdefenderGZComputerFQDN:$agent_name",
581 + "refId": "A",
582 + "timeField": "timestamp"
583 + }
584 + ],
585 + "title": "ALERTS BY CATEGORY",
586 + "transform": "table",
587 + "type": "table-old"
588 + },
589 + {
590 + "datasource": {
591 + "type": "grafana-opensearch-datasource",
592 + "uid": "replace_datasource_uid"
593 + },
594 + "fieldConfig": {
595 + "defaults": {
596 + "mappings": [
597 + {
598 + "options": {
599 + "match": "null",
600 + "result": {
601 + "text": "N/A"
602 + }
603 + },
604 + "type": "special"
605 + }
606 + ],
607 + "thresholds": {
608 + "mode": "absolute",
609 + "steps": [
610 + {
611 + "color": "green",
612 + "value": null
613 + },
614 + {
615 + "color": "red",
616 + "value": 80
617 + }
618 + ]
619 + },
620 + "unit": "none"
621 + },
622 + "overrides": []
623 + },
624 + "gridPos": {
625 + "h": 5,
626 + "w": 4,
627 + "x": 0,
628 + "y": 14
629 + },
630 + "id": 45,
631 + "links": [],
632 + "maxDataPoints": 100,
633 + "options": {
634 + "colorMode": "value",
635 + "graphMode": "none",
636 + "justifyMode": "auto",
637 + "orientation": "horizontal",
638 + "reduceOptions": {
639 + "calcs": ["max"],
640 + "fields": "",
641 + "values": false
642 + },
643 + "text": {},
644 + "textMode": "auto"
645 + },
646 + "pluginVersion": "9.2.1",
647 + "targets": [
648 + {
649 + "bucketAggs": [
650 + {
651 + "$$hashKey": "object:235",
652 + "field": "timestamp",
653 + "id": "2",
654 + "settings": {
655 + "interval": "365d",
656 + "min_doc_count": 0,
657 + "trimEdges": 0
658 + },
659 + "type": "date_histogram"
660 + }
661 + ],
662 + "datasource": {
663 + "type": "grafana-opensearch-datasource",
664 + "uid": "replace_datasource_uid"
665 + },
666 + "metrics": [
667 + {
668 + "$$hashKey": "object:233",
669 + "field": "BitdefenderGZComputerFQDN",
670 + "id": "1",
671 + "meta": {},
672 + "settings": {},
673 + "type": "cardinality"
674 + }
675 + ],
676 + "query": "BitdefenderGZComputerFQDN:$agent_name AND level:$level",
677 + "refId": "A",
678 + "timeField": "timestamp"
679 + }
680 + ],
681 + "title": "AGENTS",
682 + "type": "stat"
683 + },
684 + {
685 + "datasource": {
686 + "type": "grafana-opensearch-datasource",
687 + "uid": "replace_datasource_uid"
688 + },
689 + "fieldConfig": {
690 + "defaults": {
691 + "color": {
692 + "mode": "palette-classic"
693 + },
694 + "custom": {
695 + "axisCenteredZero": false,
696 + "axisColorMode": "text",
697 + "axisLabel": "",
698 + "axisPlacement": "auto",
699 + "barAlignment": 0,
700 + "drawStyle": "bars",
701 + "fillOpacity": 0,
702 + "gradientMode": "none",
703 + "hideFrom": {
704 + "legend": false,
705 + "tooltip": false,
706 + "viz": false
707 + },
708 + "lineInterpolation": "linear",
709 + "lineWidth": 1,
710 + "pointSize": 5,
711 + "scaleDistribution": {
712 + "type": "linear"
713 + },
714 + "showPoints": "auto",
715 + "spanNulls": false,
716 + "stacking": {
717 + "group": "A",
718 + "mode": "normal"
719 + },
720 + "thresholdsStyle": {
721 + "mode": "off"
722 + }
723 + },
724 + "mappings": [],
725 + "thresholds": {
726 + "mode": "absolute",
727 + "steps": [
728 + {
729 + "color": "green",
730 + "value": null
731 + },
732 + {
733 + "color": "red",
734 + "value": 80
735 + }
736 + ]
737 + }
738 + },
739 + "overrides": []
740 + },
741 + "gridPos": {
742 + "h": 11,
743 + "w": 20,
744 + "x": 4,
745 + "y": 14
746 + },
747 + "id": 49,
748 + "options": {
749 + "legend": {
750 + "calcs": [],
751 + "displayMode": "table",
752 + "placement": "right",
753 + "showLegend": true
754 + },
755 + "tooltip": {
756 + "mode": "single",
757 + "sort": "none"
758 + }
759 + },
760 + "targets": [
761 + {
762 + "alias": "",
763 + "bucketAggs": [
764 + {
765 + "field": "BitdefenderGZComputerFQDN",
766 + "id": "3",
767 + "settings": {
768 + "min_doc_count": "1",
769 + "order": "desc",
770 + "orderBy": "_term",
771 + "size": "10"
772 + },
773 + "type": "terms"
774 + },
775 + {
776 + "field": "timestamp",
777 + "id": "2",
778 + "settings": {
779 + "interval": "auto"
780 + },
781 + "type": "date_histogram"
782 + }
783 + ],
784 + "datasource": {
785 + "type": "grafana-opensearch-datasource",
786 + "uid": "replace_datasource_uid"
787 + },
788 + "metrics": [
789 + {
790 + "id": "1",
791 + "type": "count"
792 + }
793 + ],
794 + "query": "level:>=6 AND BitdefenderGZComputerFQDN:$agent_name",
795 + "refId": "A",
796 + "timeField": "timestamp"
797 + }
798 + ],
799 + "title": "TOP 10 AGENTS - HISTOGRAM",
800 + "transparent": true,
801 + "type": "timeseries"
802 + },
803 + {
804 + "datasource": {
805 + "type": "grafana-opensearch-datasource",
806 + "uid": "replace_datasource_uid"
807 + },
808 + "fieldConfig": {
809 + "defaults": {
810 + "mappings": [
811 + {
812 + "options": {
813 + "match": "null",
814 + "result": {
815 + "text": "N/A"
816 + }
817 + },
818 + "type": "special"
819 + }
820 + ],
821 + "thresholds": {
822 + "mode": "absolute",
823 + "steps": [
824 + {
825 + "color": "blue",
826 + "value": null
827 + }
828 + ]
829 + },
830 + "unit": "locale"
831 + },
832 + "overrides": []
833 + },
834 + "gridPos": {
835 + "h": 6,
836 + "w": 4,
837 + "x": 0,
838 + "y": 19
839 + },
840 + "id": 18,
841 + "links": [],
842 + "options": {
843 + "colorMode": "value",
844 + "graphMode": "area",
845 + "justifyMode": "auto",
846 + "orientation": "horizontal",
847 + "reduceOptions": {
848 + "calcs": ["sum"],
849 + "fields": "",
850 + "values": false
851 + },
852 + "text": {},
853 + "textMode": "auto"
854 + },
855 + "pluginVersion": "9.2.1",
856 + "targets": [
857 + {
858 + "bucketAggs": [
859 + {
860 + "$$hashKey": "object:331",
861 + "field": "timestamp",
862 + "id": "2",
863 + "settings": {
864 + "interval": "auto",
865 + "min_doc_count": 0,
866 + "trimEdges": 0
867 + },
868 + "type": "date_histogram"
869 + }
870 + ],
871 + "datasource": {
872 + "type": "grafana-opensearch-datasource",
873 + "uid": "replace_datasource_uid"
874 + },
875 + "metrics": [
876 + {
877 + "$$hashKey": "object:329",
878 + "field": "select field",
879 + "id": "1",
880 + "type": "count"
881 + }
882 + ],
883 + "query": "BitdefenderGZComputerFQDN:$agent_name AND level:$level",
884 + "refId": "A",
885 + "timeField": "timestamp"
886 + }
887 + ],
888 + "title": "EVENTS (TOTAL)",
889 + "type": "stat"
890 + },
891 + {
892 + "datasource": {
893 + "type": "grafana-opensearch-datasource",
894 + "uid": "replace_datasource_uid"
895 + },
896 + "fieldConfig": {
897 + "defaults": {
898 + "color": {
899 + "mode": "palette-classic"
900 + },
901 + "custom": {
902 + "hideFrom": {
903 + "legend": false,
904 + "tooltip": false,
905 + "viz": false
906 + }
907 + },
908 + "decimals": 0,
909 + "mappings": [],
910 + "unit": "short"
911 + },
912 + "overrides": [
913 + {
914 + "matcher": {
915 + "id": "byName",
916 + "options": "1"
917 + },
918 + "properties": [
919 + {
920 + "id": "color",
921 + "value": {
922 + "fixedColor": "#C8F2C2",
923 + "mode": "fixed"
924 + }
925 + }
926 + ]
927 + },
928 + {
929 + "matcher": {
930 + "id": "byName",
931 + "options": "2"
932 + },
933 + "properties": [
934 + {
935 + "id": "color",
936 + "value": {
937 + "fixedColor": "#96D98D",
938 + "mode": "fixed"
939 + }
940 + }
941 + ]
942 + },
943 + {
944 + "matcher": {
945 + "id": "byName",
946 + "options": "3"
947 + },
948 + "properties": [
949 + {
950 + "id": "color",
951 + "value": {
952 + "fixedColor": "#56A64B",
953 + "mode": "fixed"
954 + }
955 + }
956 + ]
957 + },
958 + {
959 + "matcher": {
960 + "id": "byName",
961 + "options": "4"
962 + },
963 + "properties": [
964 + {
965 + "id": "color",
966 + "value": {
967 + "fixedColor": "#37872D",
968 + "mode": "fixed"
969 + }
970 + }
971 + ]
972 + },
973 + {
974 + "matcher": {
975 + "id": "byName",
976 + "options": "5"
977 + },
978 + "properties": [
979 + {
980 + "id": "color",
981 + "value": {
982 + "fixedColor": "#FFF899",
983 + "mode": "fixed"
984 + }
985 + }
986 + ]
987 + },
988 + {
989 + "matcher": {
990 + "id": "byName",
991 + "options": "7"
992 + },
993 + "properties": [
994 + {
995 + "id": "color",
996 + "value": {
997 + "fixedColor": "#F2CC0C",
998 + "mode": "fixed"
999 + }
1000 + }
1001 + ]
1002 + },
1003 + {
1004 + "matcher": {
1005 + "id": "byName",
1006 + "options": "9"
1007 + },
1008 + "properties": [
1009 + {
1010 + "id": "color",
1011 + "value": {
1012 + "fixedColor": "#FF9830",
1013 + "mode": "fixed"
1014 + }
1015 + }
1016 + ]
1017 + },
1018 + {
1019 + "matcher": {
1020 + "id": "byName",
1021 + "options": "10"
1022 + },
1023 + "properties": [
1024 + {
1025 + "id": "color",
1026 + "value": {
1027 + "fixedColor": "#FF9830",
1028 + "mode": "fixed"
1029 + }
1030 + }
1031 + ]
1032 + },
1033 + {
1034 + "matcher": {
1035 + "id": "byName",
1036 + "options": "12"
1037 + },
1038 + "properties": [
1039 + {
1040 + "id": "color",
1041 + "value": {
1042 + "fixedColor": "#F2495C",
1043 + "mode": "fixed"
1044 + }
1045 + }
1046 + ]
1047 + },
1048 + {
1049 + "matcher": {
1050 + "id": "byName",
1051 + "options": "13"
1052 + },
1053 + "properties": [
1054 + {
1055 + "id": "color",
1056 + "value": {
1057 + "fixedColor": "#FF7383",
1058 + "mode": "fixed"
1059 + }
1060 + }
1061 + ]
1062 + }
1063 + ]
1064 + },
1065 + "gridPos": {
1066 + "h": 12,
1067 + "w": 6,
1068 + "x": 0,
1069 + "y": 25
1070 + },
1071 + "id": 23,
1072 + "links": [],
1073 + "maxDataPoints": 3,
1074 + "options": {
1075 + "displayLabels": [],
1076 + "legend": {
1077 + "calcs": [],
1078 + "displayMode": "table",
1079 + "placement": "right",
1080 + "showLegend": true,
1081 + "values": ["value", "percent"]
1082 + },
1083 + "pieType": "donut",
1084 + "reduceOptions": {
1085 + "calcs": ["sum"],
1086 + "fields": "",
1087 + "values": false
1088 + },
1089 + "text": {},
1090 + "tooltip": {
1091 + "mode": "single",
1092 + "sort": "none"
1093 + }
1094 + },
1095 + "targets": [
1096 + {
1097 + "bucketAggs": [
1098 + {
1099 + "$$hashKey": "object:235",
1100 + "fake": true,
1101 + "field": "level",
1102 + "id": "3",
1103 + "settings": {
1104 + "min_doc_count": 1,
1105 + "order": "desc",
1106 + "orderBy": "_count",
1107 + "size": "10"
1108 + },
1109 + "type": "terms"
1110 + },
1111 + {
1112 + "$$hashKey": "object:236",
1113 + "field": "timestamp",
1114 + "id": "2",
1115 + "settings": {
1116 + "interval": "auto",
1117 + "min_doc_count": 0,
1118 + "trimEdges": 0
1119 + },
1120 + "type": "date_histogram"
1121 + }
1122 + ],
1123 + "datasource": {
1124 + "type": "grafana-opensearch-datasource",
1125 + "uid": "replace_datasource_uid"
1126 + },
1127 + "metrics": [
1128 + {
1129 + "$$hashKey": "object:233",
1130 + "field": "select field",
1131 + "id": "1",
1132 + "meta": {},
1133 + "settings": {},
1134 + "type": "count"
1135 + }
1136 + ],
1137 + "query": "BitdefenderGZComputerFQDN:$agent_name AND level:$level",
1138 + "refId": "A",
1139 + "timeField": "timestamp"
1140 + }
1141 + ],
1142 + "title": "SECURITY EVENTS BY ALERT LEVEL",
1143 + "type": "piechart"
1144 + },
1145 + {
1146 + "datasource": {
1147 + "type": "grafana-opensearch-datasource",
1148 + "uid": "replace_datasource_uid"
1149 + },
1150 + "fieldConfig": {
1151 + "defaults": {
1152 + "color": {
1153 + "mode": "palette-classic"
1154 + },
1155 + "custom": {
1156 + "axisCenteredZero": false,
1157 + "axisColorMode": "text",
1158 + "axisLabel": "",
1159 + "axisPlacement": "auto",
1160 + "barAlignment": 0,
1161 + "drawStyle": "bars",
1162 + "fillOpacity": 0,
1163 + "gradientMode": "none",
1164 + "hideFrom": {
1165 + "legend": false,
1166 + "tooltip": false,
1167 + "viz": false
1168 + },
1169 + "lineInterpolation": "linear",
1170 + "lineWidth": 1,
1171 + "pointSize": 5,
1172 + "scaleDistribution": {
1173 + "type": "linear"
1174 + },
1175 + "showPoints": "auto",
1176 + "spanNulls": false,
1177 + "stacking": {
1178 + "group": "A",
1179 + "mode": "normal"
1180 + },
1181 + "thresholdsStyle": {
1182 + "mode": "off"
1183 + }
1184 + },
1185 + "mappings": [],
1186 + "thresholds": {
1187 + "mode": "absolute",
1188 + "steps": [
1189 + {
1190 + "color": "green",
1191 + "value": null
1192 + },
1193 + {
1194 + "color": "red",
1195 + "value": 80
1196 + }
1197 + ]
1198 + }
1199 + },
1200 + "overrides": []
1201 + },
1202 + "gridPos": {
1203 + "h": 12,
1204 + "w": 18,
1205 + "x": 6,
1206 + "y": 25
1207 + },
1208 + "id": 50,
1209 + "options": {
1210 + "legend": {
1211 + "calcs": [],
1212 + "displayMode": "table",
1213 + "placement": "right",
1214 + "showLegend": true
1215 + },
1216 + "tooltip": {
1217 + "mode": "single",
1218 + "sort": "none"
1219 + }
1220 + },
1221 + "targets": [
1222 + {
1223 + "alias": "",
1224 + "bucketAggs": [
1225 + {
1226 + "field": "level",
1227 + "id": "3",
1228 + "settings": {
1229 + "min_doc_count": "1",
1230 + "order": "desc",
1231 + "orderBy": "_count",
1232 + "size": "10"
1233 + },
1234 + "type": "terms"
1235 + },
1236 + {
1237 + "field": "timestamp",
1238 + "id": "2",
1239 + "settings": {
1240 + "interval": "auto"
1241 + },
1242 + "type": "date_histogram"
1243 + }
1244 + ],
1245 + "datasource": {
1246 + "type": "grafana-opensearch-datasource",
1247 + "uid": "replace_datasource_uid"
1248 + },
1249 + "metrics": [
1250 + {
1251 + "id": "1",
1252 + "type": "count"
1253 + }
1254 + ],
1255 + "query": "BitdefenderGZComputerFQDN:$agent_name AND level:$level",
1256 + "refId": "A",
1257 + "timeField": "timestamp"
1258 + }
1259 + ],
1260 + "title": "EVENTS SEVERITY - HISTOGRAM",
1261 + "type": "timeseries"
1262 + },
1263 + {
1264 + "datasource": {
1265 + "type": "grafana-opensearch-datasource",
1266 + "uid": "replace_datasource_uid"
1267 + },
1268 + "fieldConfig": {
1269 + "defaults": {
1270 + "color": {
1271 + "mode": "thresholds"
1272 + },
1273 + "custom": {
1274 + "align": "auto",
1275 + "displayMode": "auto",
1276 + "inspect": false
1277 + },
1278 + "decimals": 2,
1279 + "displayName": "",
1280 + "mappings": [],
1281 + "thresholds": {
1282 + "mode": "absolute",
1283 + "steps": [
1284 + {
1285 + "color": "green",
1286 + "value": null
1287 + },
1288 + {
1289 + "color": "red",
1290 + "value": 80
1291 + }
1292 + ]
1293 + },
1294 + "unit": "short"
1295 + },
1296 + "overrides": [
1297 + {
1298 + "matcher": {
1299 + "id": "byName",
1300 + "options": "Time"
1301 + },
1302 + "properties": [
1303 + {
1304 + "id": "displayName",
1305 + "value": "Time"
1306 + },
1307 + {
1308 + "id": "unit",
1309 + "value": "time: YYYY-MM-DD HH:mm:ss"
1310 + },
1311 + {
1312 + "id": "custom.align"
1313 + }
1314 + ]
1315 + },
1316 + {
1317 + "matcher": {
1318 + "id": "byName",
1319 + "options": "Count"
1320 + },
1321 + "properties": [
1322 + {
1323 + "id": "displayName",
1324 + "value": "Events"
1325 + },
1326 + {
1327 + "id": "unit",
1328 + "value": "short"
1329 + },
1330 + {
1331 + "id": "decimals",
1332 + "value": -1
1333 + },
1334 + {
1335 + "id": "custom.align"
1336 + }
1337 + ]
1338 + },
1339 + {
1340 + "matcher": {
1341 + "id": "byName",
1342 + "options": "BitdefenderGZModule"
1343 + },
1344 + "properties": [
1345 + {
1346 + "id": "displayName",
1347 + "value": "MODULE"
1348 + },
1349 + {
1350 + "id": "unit",
1351 + "value": "short"
1352 + },
1353 + {
1354 + "id": "decimals",
1355 + "value": 2
1356 + },
1357 + {
1358 + "id": "custom.align"
1359 + },
1360 + {
1361 + "id": "mappings",
1362 + "value": [
1363 + {
1364 + "options": {
1365 + "BITDEFENDER, agent_flooding": {
1366 + "index": 33,
1367 + "text": "BITDEFENDER Agent - Event Queue Flooding"
1368 + },
1369 + "apache, web, modsecurity": {
1370 + "index": 7,
1371 + "text": "Apache ModSec"
1372 + },
1373 + "dnsstat, dnsstat_alert": {
1374 + "index": 47,
1375 + "text": "Domain Stats - Alert"
1376 + },
1377 + "dnsstat, dnsstat_error": {
1378 + "index": 41,
1379 + "text": "Domain Stats - Entry Not found in RDAP"
1380 + },
1381 + "docker, docker-error": {
1382 + "index": 43,
1383 + "text": "Docker Error"
1384 + },
1385 + "linux, docker, falco": {
1386 + "index": 56,
1387 + "text": "Linux Docker: Container Event"
1388 + },
1389 + "linux, packetbeat, dns": {
1390 + "index": 58,
1391 + "text": "Linux - DNS Request"
1392 + },
1393 + "linux, packetbeat, http": {
1394 + "index": 73,
1395 + "text": "Linux Packetbeat - HTTP Connection"
1396 + },
1397 + "linux, packetbeat, tls": {
1398 + "index": 72,
1399 + "text": "Linux Packetbeat - HTTPS Connection"
1400 + },
1401 + "linux, sysmon, sysmon_event1": {
1402 + "index": 3,
1403 + "text": "Linux Sysmon - Process Started"
1404 + },
1405 + "linux, sysmon, sysmon_event3": {
1406 + "index": 2,
1407 + "text": "Linux Sysmon - Network Connection"
1408 + },
1409 + "linux, sysmon, sysmon_event5": {
1410 + "index": 1,
1411 + "text": "Linux Sysmon - Process Terminated"
1412 + },
1413 + "linux, sysmon, sysmon_event9": {
1414 + "index": 46,
1415 + "text": "Linux Sysmon - RawAccessRead"
1416 + },
1417 + "linux, sysmon, sysmon_event_11": {
1418 + "index": 4,
1419 + "text": "Linux Sysmon - FileCreate"
1420 + },
1421 + "linux, sysmon, sysmon_event_16": {
1422 + "index": 6,
1423 + "text": "Linux Sysmon - Sysmon Config Changed"
1424 + },
1425 + "linux, sysmon, sysmon_event_23": {
1426 + "index": 5,
1427 + "text": "Linux Sysmon - File Removed"
1428 + },
1429 + "local, systemd": {
1430 + "index": 74,
1431 + "text": "Linux Systemd"
1432 + },
1433 + "openvpn, authentication_success": {
1434 + "index": 68,
1435 + "text": "OpenVPN Client - Auth Success"
1436 + },
1437 + "ossec": {
1438 + "index": 15,
1439 + "text": "OSSEC Event"
1440 + },
1441 + "ossec, rootcheck": {
1442 + "index": 19,
1443 + "text": "OSSEC - Rootcheck"
1444 + },
1445 + "ossec, syscheck, syscheck_entry_added, syscheck_file": {
1446 + "index": 9,
1447 + "text": "Syscheck - File Added"
1448 + },
1449 + "ossec, syscheck, syscheck_entry_added, syscheck_registry": {
1450 + "index": 39,
1451 + "text": "Syscheck - Windows Registry (Entry Added)"
1452 + },
1453 + "ossec, syscheck, syscheck_entry_deleted, syscheck_file": {
1454 + "index": 52,
1455 + "text": "Syscheck - File Deleted"
1456 + },
1457 + "ossec, syscheck, syscheck_entry_deleted, syscheck_registry": {
1458 + "index": 45,
1459 + "text": "Syscheck - Windows Registry (Entry Deleted)"
1460 + },
1461 + "ossec, syscheck, syscheck_entry_modified, syscheck_file": {
1462 + "index": 14,
1463 + "text": "Syscheck - File Modified"
1464 + },
1465 + "ossec, syscheck, syscheck_entry_modified, syscheck_registry": {
1466 + "index": 30,
1467 + "text": "Syscheck - Windows Registry (Entry Modified)"
1468 + },
1469 + "pam, syslog": {
1470 + "index": 18,
1471 + "text": "Linux PAM"
1472 + },
1473 + "pam, syslog, authentication_failed": {
1474 + "index": 67,
1475 + "text": "Linux PAM - Auth Failed"
1476 + },
1477 + "pam, syslog, authentication_success": {
1478 + "index": 12,
1479 + "text": "Linux PAM - Auth Success"
1480 + },
1481 + "sca": {
1482 + "index": 17,
1483 + "text": "Security Config Assessment"
1484 + },
1485 + "syslog, adduser": {
1486 + "index": 54,
1487 + "text": "Linux - User Added"
1488 + },
1489 + "syslog, dpkg": {
1490 + "index": 11,
1491 + "text": "Lunux dpkg"
1492 + },
1493 + "syslog, dpkg, config_changed": {
1494 + "index": 10,
1495 + "text": "Linux dpkg - Config Changed"
1496 + },
1497 + "syslog, errors, service_availability": {
1498 + "index": 75,
1499 + "text": "Linux Syslog - System Error"
1500 + },
1501 + "syslog, linuxkernel": {
1502 + "index": 57,
1503 + "text": "Linux - Kernel Event"
1504 + },
1505 + "syslog, linuxkernel, promisc": {
1506 + "index": 29,
1507 + "text": "Linux Kernel - Promisc. Interface"
1508 + },
1509 + "syslog, sshd, authentication_success": {
1510 + "index": 13,
1511 + "text": "SSH - Auth Success"
1512 + },
1513 + "syslog, sshd, recon": {
1514 + "index": 51,
1515 + "text": "Linux - SSH Daemon Alert"
1516 + },
1517 + "syslog, sudo": {
1518 + "index": 16,
1519 + "text": "Lunux - Sudo"
1520 + },
1521 + "threat_intel, alienvault, otx_alert": {
1522 + "index": 63,
1523 + "text": "Threat Intel - AlienVault OTX IoC Alert"
1524 + },
1525 + "threat_intel, misp, misp_alert": {
1526 + "index": 40,
1527 + "text": "Threat Intel - MISP IoC Alert"
1528 + },
1529 + "threat_intel, opencti, opencti_alert": {
1530 + "index": 62,
1531 + "text": "Threat Intel - OpenCTI IoC Alert"
1532 + },
1533 + "threat_intel, opencti, opencti_error": {
1534 + "index": 64,
1535 + "text": "Threat Intel - OpenCTI API Error"
1536 + },
1537 + "usb": {
1538 + "index": 69,
1539 + "text": "USB Port Event"
1540 + },
1541 + "vulnerability-detector": {
1542 + "index": 0,
1543 + "text": "Vulnerability Detector"
1544 + },
1545 + "vulnerability-detector, snyk": {
1546 + "index": 55,
1547 + "text": "Vulnerability Detector - Docker Images"
1548 + },
1549 + "windows, inventory": {
1550 + "index": 27,
1551 + "text": "Windows Agent Inventory"
1552 + },
1553 + "windows, sysmon, sysmon_event1, windows_sysmon_event1": {
1554 + "index": 48,
1555 + "text": "Windows Sysmon - Process Started"
1556 + },
1557 + "windows, sysmon, sysmon_event1, windows_sysmon_event1, sysmon_anomaly": {
1558 + "index": 77,
1559 + "text": "Windows Sysmon - Process Started Anomaly"
1560 + },
1561 + "windows, sysmon, sysmon_event2": {
1562 + "index": 78,
1563 + "text": "Windows Sysmon - A Process changed File Creation Time"
1564 + },
1565 + "windows, sysmon, sysmon_event3": {
1566 + "index": 36,
1567 + "text": "Windows Sysmon - Network Connection"
1568 + },
1569 + "windows, sysmon, sysmon_event3, sysmon_anomaly": {
1570 + "index": 76,
1571 + "text": "Windows Sysmon - Network Connection Anomaly"
1572 + },
1573 + "windows, sysmon, sysmon_event7": {
1574 + "index": 25,
1575 + "text": "Windows Sysmon - DLL SideLoading"
1576 + },
1577 + "windows, sysmon, sysmon_event_10": {
1578 + "index": 32,
1579 + "text": "Windows Sysmon - Process Injection"
1580 + },
1581 + "windows, sysmon, sysmon_event_11": {
1582 + "index": 20,
1583 + "text": "Windows Sysmon - FileCreate"
1584 + },
1585 + "windows, sysmon, sysmon_event_12": {
1586 + "index": 23,
1587 + "text": "Windows Sysmon - RegistryEvent (Object create and delete)"
1588 + },
1589 + "windows, sysmon, sysmon_event_13": {
1590 + "index": 24,
1591 + "text": "Windows Sysmon - RegistryEvent (ValueSet)"
1592 + },
1593 + "windows, sysmon, sysmon_event_15": {
1594 + "index": 61,
1595 + "text": "Windows Sysmon - FileCreateStreamHash"
1596 + },
1597 + "windows, sysmon, sysmon_event_17": {
1598 + "index": 70,
1599 + "text": "Windows Sysmon - Pipe Created"
1600 + },
1601 + "windows, sysmon, sysmon_event_22": {
1602 + "index": 38,
1603 + "text": "Windows Sysmon - DNS Request"
1604 + },
1605 + "windows, sysmon, sysmon_event_23": {
1606 + "index": 28,
1607 + "text": "Windows Sysmon - File Removed"
1608 + },
1609 + "windows, sysmon, sysmon_event_25": {
1610 + "index": 71,
1611 + "text": "Windows Sysmon - Process Tampering"
1612 + },
1613 + "windows, sysmon, sysmon_process-anomalies": {
1614 + "index": 53,
1615 + "text": "Windows Sysmon - Process Anomalies"
1616 + },
1617 + "windows, system_error": {
1618 + "index": 49,
1619 + "text": "Windows - System Error"
1620 + },
1621 + "windows, windows_application": {
1622 + "index": 31,
1623 + "text": "WinEvtLogs - Application"
1624 + },
1625 + "windows, windows_application, system_error": {
1626 + "index": 59,
1627 + "text": "WinEvtLogs - Application Error"
1628 + },
1629 + "windows, windows_autoruns": {
1630 + "index": 37,
1631 + "text": "Windows Persistent Footholds"
1632 + },
1633 + "windows, windows_defender": {
1634 + "index": 35,
1635 + "text": "Windows Defender"
1636 + },
1637 + "windows, windows_firewall, firewall": {
1638 + "index": 60,
1639 + "text": "Windows - Windows Firewall"
1640 + },
1641 + "windows, windows_logonsessions": {
1642 + "index": 26,
1643 + "text": "Windows Logon Sessions (Snapshot)"
1644 + },
1645 + "windows, windows_powershell": {
1646 + "index": 50,
1647 + "text": "Windows - PowerShell"
1648 + },
1649 + "windows, windows_security": {
1650 + "index": 22,
1651 + "text": "WinEvtLogs - Security"
1652 + },
1653 + "windows, windows_security, authentication_failed": {
1654 + "index": 65,
1655 + "text": "Windows - Failed Authentication"
1656 + },
1657 + "windows, windows_security, authentication_success": {
1658 + "index": 21,
1659 + "text": "Windows - Successful Auths"
1660 + },
1661 + "windows, windows_sigcheck": {
1662 + "index": 42,
1663 + "text": "Windows Exec Analysis"
1664 + },
1665 + "windows, windows_system": {
1666 + "index": 44,
1667 + "text": "WinEvtLogs - System"
1668 + },
1669 + "windows, windows_system, policy_changed": {
1670 + "index": 34,
1671 + "text": "Windows Group Policy"
1672 + },
1673 + "windows, windows_system, system_error": {
1674 + "index": 66,
1675 + "text": "Windows - System Error"
1676 + },
1677 + "yara": {
1678 + "index": 8,
1679 + "text": "Yara Malware Scanner"
1680 + }
1681 + },
1682 + "type": "value"
1683 + }
1684 + ]
1685 + }
1686 + ]
1687 + },
1688 + {
1689 + "matcher": {
1690 + "id": "byName",
1691 + "options": "Rule Groups"
1692 + },
1693 + "properties": [
1694 + {
1695 + "id": "custom.width",
1696 + "value": 302
1697 + }
1698 + ]
1699 + }
1700 + ]
1701 + },
1702 + "gridPos": {
1703 + "h": 14,
1704 + "w": 6,
1705 + "x": 0,
1706 + "y": 37
1707 + },
1708 + "id": 24,
1709 + "links": [],
1710 + "options": {
1711 + "footer": {
1712 + "fields": "",
1713 + "reducer": ["sum"],
1714 + "show": false
1715 + },
1716 + "showHeader": true,
1717 + "sortBy": []
1718 + },
1719 + "pluginVersion": "9.2.1",
1720 + "targets": [
1721 + {
1722 + "bucketAggs": [
1723 + {
1724 + "$$hashKey": "object:332",
1725 + "field": "BitdefenderGZModule",
1726 + "id": "2",
1727 + "settings": {
1728 + "min_doc_count": 1,
1729 + "order": "desc",
1730 + "orderBy": "_count",
1731 + "size": "0"
1732 + },
1733 + "type": "terms"
1734 + }
1735 + ],
1736 + "datasource": {
1737 + "type": "grafana-opensearch-datasource",
1738 + "uid": "replace_datasource_uid"
1739 + },
1740 + "metrics": [
1741 + {
1742 + "$$hashKey": "object:330",
1743 + "field": "select field",
1744 + "id": "1",
1745 + "meta": {},
1746 + "settings": {},
1747 + "type": "count"
1748 + }
1749 + ],
1750 + "query": "BitdefenderGZComputerFQDN:$agent_name AND level:$level",
1751 + "refId": "A",
1752 + "timeField": "timestamp"
1753 + }
1754 + ],
1755 + "title": "EVENTS BY MODULE",
1756 + "transformations": [
1757 + {
1758 + "id": "merge",
1759 + "options": {
1760 + "reducers": []
1761 + }
1762 + },
1763 + {
1764 + "id": "organize",
1765 + "options": {
1766 + "excludeByName": {},
1767 + "indexByName": {},
1768 + "renameByName": {
1769 + "BitdefenderGZModule": "MODULE"
1770 + }
1771 + }
1772 + }
1773 + ],
1774 + "type": "table"
1775 + },
1776 + {
1777 + "datasource": {
1778 + "type": "grafana-opensearch-datasource",
1779 + "uid": "replace_datasource_uid"
1780 + },
1781 + "fieldConfig": {
1782 + "defaults": {
1783 + "color": {
1784 + "mode": "palette-classic"
1785 + },
1786 + "custom": {
1787 + "axisCenteredZero": false,
1788 + "axisColorMode": "text",
1789 + "axisLabel": "",
1790 + "axisPlacement": "auto",
1791 + "barAlignment": 0,
1792 + "drawStyle": "bars",
1793 + "fillOpacity": 0,
1794 + "gradientMode": "none",
1795 + "hideFrom": {
1796 + "legend": false,
1797 + "tooltip": false,
1798 + "viz": false
1799 + },
1800 + "lineInterpolation": "linear",
1801 + "lineWidth": 1,
1802 + "pointSize": 5,
1803 + "scaleDistribution": {
1804 + "type": "linear"
1805 + },
1806 + "showPoints": "auto",
1807 + "spanNulls": false,
1808 + "stacking": {
1809 + "group": "A",
1810 + "mode": "normal"
1811 + },
1812 + "thresholdsStyle": {
1813 + "mode": "off"
1814 + }
1815 + },
1816 + "mappings": [],
1817 + "thresholds": {
1818 + "mode": "absolute",
1819 + "steps": [
1820 + {
1821 + "color": "green",
1822 + "value": null
1823 + },
1824 + {
1825 + "color": "red",
1826 + "value": 80
1827 + }
1828 + ]
1829 + }
1830 + },
1831 + "overrides": []
1832 + },
1833 + "gridPos": {
1834 + "h": 14,
1835 + "w": 18,
1836 + "x": 6,
1837 + "y": 37
1838 + },
1839 + "id": 51,
1840 + "options": {
1841 + "legend": {
1842 + "calcs": [],
1843 + "displayMode": "table",
1844 + "placement": "right",
1845 + "showLegend": true
1846 + },
1847 + "tooltip": {
1848 + "mode": "single",
1849 + "sort": "none"
1850 + }
1851 + },
1852 + "targets": [
1853 + {
1854 + "alias": "",
1855 + "bucketAggs": [
1856 + {
1857 + "field": "BitdefenderGZModule",
1858 + "id": "3",
1859 + "settings": {
1860 + "min_doc_count": "1",
1861 + "order": "desc",
1862 + "orderBy": "_count",
1863 + "size": "10"
1864 + },
1865 + "type": "terms"
1866 + },
1867 + {
1868 + "field": "timestamp",
1869 + "id": "2",
1870 + "settings": {
1871 + "interval": "auto"
1872 + },
1873 + "type": "date_histogram"
1874 + }
1875 + ],
1876 + "datasource": {
1877 + "type": "grafana-opensearch-datasource",
1878 + "uid": "replace_datasource_uid"
1879 + },
1880 + "metrics": [
1881 + {
1882 + "id": "1",
1883 + "type": "count"
1884 + }
1885 + ],
1886 + "query": "BitdefenderGZComputerFQDN:$agent_name AND level:$level",
1887 + "refId": "A",
1888 + "timeField": "timestamp"
1889 + }
1890 + ],
1891 + "title": "EVENTS BY MODULE (TOP 10) - HISTOGRAM",
1892 + "transparent": true,
1893 + "type": "timeseries"
1894 + },
1895 + {
1896 + "datasource": {
1897 + "type": "grafana-opensearch-datasource",
1898 + "uid": "replace_datasource_uid"
1899 + },
1900 + "fieldConfig": {
1901 + "defaults": {
1902 + "color": {
1903 + "mode": "thresholds"
1904 + },
1905 + "custom": {
1906 + "align": "auto",
1907 + "displayMode": "auto",
1908 + "filterable": true,
1909 + "inspect": false
1910 + },
1911 + "mappings": [],
1912 + "thresholds": {
1913 + "mode": "absolute",
1914 + "steps": [
1915 + {
1916 + "color": "green",
1917 + "value": null
1918 + },
1919 + {
1920 + "color": "red",
1921 + "value": 80
1922 + }
1923 + ]
1924 + }
1925 + },
1926 + "overrides": [
1927 + {
1928 + "matcher": {
1929 + "id": "byName",
1930 + "options": "timestamp"
1931 + },
1932 + "properties": [
1933 + {
1934 + "id": "displayName",
1935 + "value": "Date/Time"
1936 + },
1937 + {
1938 + "id": "unit",
1939 + "value": "time: YYYY-MM-DD HH:mm:ss"
1940 + },
1941 + {
1942 + "id": "custom.align"
1943 + }
1944 + ]
1945 + },
1946 + {
1947 + "matcher": {
1948 + "id": "byName",
1949 + "options": "agent_name"
1950 + },
1951 + "properties": [
1952 + {
1953 + "id": "displayName",
1954 + "value": "AGENT"
1955 + },
1956 + {
1957 + "id": "unit",
1958 + "value": "short"
1959 + },
1960 + {
1961 + "id": "decimals",
1962 + "value": 2
1963 + },
1964 + {
1965 + "id": "custom.align"
1966 + }
1967 + ]
1968 + },
1969 + {
1970 + "matcher": {
1971 + "id": "byName",
1972 + "options": "agent_ip"
1973 + },
1974 + "properties": [
1975 + {
1976 + "id": "displayName",
1977 + "value": "IP ADDRESS"
1978 + },
1979 + {
1980 + "id": "unit",
1981 + "value": "short"
1982 + },
1983 + {
1984 + "id": "decimals",
1985 + "value": 2
1986 + },
1987 + {
1988 + "id": "custom.align"
1989 + }
1990 + ]
1991 + },
1992 + {
1993 + "matcher": {
1994 + "id": "byName",
1995 + "options": "level"
1996 + },
1997 + "properties": [
1998 + {
1999 + "id": "displayName",
2000 + "value": "Level"
2001 + },
2002 + {
2003 + "id": "unit",
2004 + "value": "short"
2005 + },
2006 + {
2007 + "id": "decimals",
2008 + "value": -1
2009 + },
2010 + {
2011 + "id": "custom.displayMode",
2012 + "value": "color-background"
2013 + },
2014 + {
2015 + "id": "custom.align"
2016 + },
2017 + {
2018 + "id": "thresholds",
2019 + "value": {
2020 + "mode": "absolute",
2021 + "steps": [
2022 + {
2023 + "color": "#37872D",
2024 + "value": null
2025 + },
2026 + {
2027 + "color": "rgba(237, 129, 40, 0.89)",
2028 + "value": 7
2029 + },
2030 + {
2031 + "color": "rgba(245, 54, 54, 0.9)",
2032 + "value": 12
2033 + }
2034 + ]
2035 + }
2036 + }
2037 + ]
2038 + },
2039 + {
2040 + "matcher": {
2041 + "id": "byName",
2042 + "options": "rule_description"
2043 + },
2044 + "properties": [
2045 + {
2046 + "id": "displayName",
2047 + "value": "RULE DESCRIPTION"
2048 + },
2049 + {
2050 + "id": "unit",
2051 + "value": "short"
2052 + },
2053 + {
2054 + "id": "decimals",
2055 + "value": 2
2056 + },
2057 + {
2058 + "id": "custom.align"
2059 + }
2060 + ]
2061 + },
2062 + {
2063 + "matcher": {
2064 + "id": "byName",
2065 + "options": "Date/Time"
2066 + },
2067 + "properties": [
2068 + {
2069 + "id": "custom.width",
2070 + "value": 242
2071 + }
2072 + ]
2073 + },
2074 + {
2075 + "matcher": {
2076 + "id": "byName",
2077 + "options": "AGENT"
2078 + },
2079 + "properties": [
2080 + {
2081 + "id": "custom.width",
2082 + "value": 160
2083 + }
2084 + ]
2085 + },
2086 + {
2087 + "matcher": {
2088 + "id": "byName",
2089 + "options": "MITRE TACTIC"
2090 + },
2091 + "properties": [
2092 + {
2093 + "id": "custom.width",
2094 + "value": 332
2095 + }
2096 + ]
2097 + },
2098 + {
2099 + "matcher": {
2100 + "id": "byName",
2101 + "options": "Level"
2102 + },
2103 + "properties": [
2104 + {
2105 + "id": "custom.width",
2106 + "value": 122
2107 + }
2108 + ]
2109 + },
2110 + {
2111 + "matcher": {
2112 + "id": "byName",
2113 + "options": "IP ADDRESS"
2114 + },
2115 + "properties": [
2116 + {
2117 + "id": "custom.width",
2118 + "value": 163
2119 + }
2120 + ]
2121 + },
2122 + {
2123 + "matcher": {
2124 + "id": "byName",
2125 + "options": "MITRE TECHNIQUE"
2126 + },
2127 + "properties": [
2128 + {
2129 + "id": "custom.width",
2130 + "value": 312
2131 + }
2132 + ]
2133 + },
2134 + {
2135 + "matcher": {
2136 + "id": "byName",
2137 + "options": "rule_id"
2138 + },
2139 + "properties": [
2140 + {
2141 + "id": "custom.width",
2142 + "value": 96
2143 + }
2144 + ]
2145 + },
2146 + {
2147 + "matcher": {
2148 + "id": "byName",
2149 + "options": "EVENT ID"
2150 + },
2151 + "properties": [
2152 + {
2153 + "id": "links",
2154 + "value": [
2155 + {
2156 + "targetBlank": true,
2157 + "title": "VIEW EVENT DETAILS",
2158 + "url": "https://grafana.company.local/explore?left=%7B%22datasource%22:%22BITDEFENDER%22,%22queries%22:%5B%7B%22refId%22:%22A%22,%22query%22:%22_id:${__value.text}%22,%22alias%22:%22%22,%22metrics%22:%5B%7B%22id%22:%221%22,%22type%22:%22logs%22,%22settings%22:%7B%22limit%22:%22500%22%7D%7D%5D,%22bucketAggs%22:%5B%5D,%22timeField%22:%22timestamp%22%7D%5D,%22range%22:%7B%22from%22:%22now-6h%22,%22to%22:%22now%22%7D%7D"
2159 + }
2160 + ]
2161 + }
2162 + ]
2163 + }
2164 + ]
2165 + },
2166 + "gridPos": {
2167 + "h": 16,
2168 + "w": 24,
2169 + "x": 0,
2170 + "y": 51
2171 + },
2172 + "id": 27,
2173 + "options": {
2174 + "footer": {
2175 + "fields": "",
2176 + "reducer": ["sum"],
2177 + "show": false
2178 + },
2179 + "showHeader": true,
2180 + "sortBy": [
2181 + {
2182 + "desc": true,
2183 + "displayName": "Date/Time"
2184 + }
2185 + ]
2186 + },
2187 + "pluginVersion": "9.2.1",
2188 + "targets": [
2189 + {
2190 + "bucketAggs": [],
2191 + "datasource": {
2192 + "type": "grafana-opensearch-datasource",
2193 + "uid": "replace_datasource_uid"
2194 + },
2195 + "metrics": [
2196 + {
2197 + "id": "1",
2198 + "settings": {
2199 + "size": "250"
2200 + },
2201 + "type": "raw_data"
2202 + }
2203 + ],
2204 + "query": "BitdefenderGZComputerFQDN:$agent_name AND level:$level",
2205 + "refId": "A",
2206 + "timeField": "timestamp"
2207 + }
2208 + ],
2209 + "title": "EVENTS",
2210 + "transformations": [
2211 + {
2212 + "id": "merge",
2213 + "options": {
2214 + "reducers": []
2215 + }
2216 + },
2217 + {
2218 + "id": "organize",
2219 + "options": {
2220 + "excludeByName": {
2221 + "@metadata_beat": true,
2222 + "@metadata_type": true,
2223 + "@metadata_version": true,
2224 + "BitdefenderGZAttCkId": true,
2225 + "BitdefenderGZAttackEntry": true,
2226 + "BitdefenderGZCompanyId": true,
2227 + "BitdefenderGZDetectionTime": true,
2228 + "BitdefenderGZEndpointId": true,
2229 + "BitdefenderGZFileHashSha256": true,
2230 + "BitdefenderGZIncidentId": true,
2231 + "BitdefenderGZMalwareHash": true,
2232 + "BitdefenderGZMalwareName": true,
2233 + "BitdefenderGZMalwareType": true,
2234 + "BitdefenderGZModule": false,
2235 + "BitdefenderGZParentPath": true,
2236 + "BitdefenderGZParentPid": true,
2237 + "BitdefenderGZProcessCommandLine": true,
2238 + "BitdefenderGZScanEngineType": true,
2239 + "BitdefenderGZSeverityScore": true,
2240 + "BitdefenderGZSignaturesNumber": true,
2241 + "IMPHASH": true,
2242 + "MD5": true,
2243 + "SHA1": true,
2244 + "SHA256": true,
2245 + "_id": false,
2246 + "_index": true,
2247 + "_type": true,
2248 + "act": true,
2249 + "agent_ephemeral_id": true,
2250 + "agent_hostname": true,
2251 + "agent_id": true,
2252 + "agent_ip_city_name": true,
2253 + "agent_ip_country_code": true,
2254 + "agent_ip_geolocation": true,
2255 + "agent_name": false,
2256 + "agent_type": true,
2257 + "agent_version": true,
2258 + "beats_type": true,
2259 + "collector_node_id": true,
2260 + "data_alert_action": true,
2261 + "data_alert_category": true,
2262 + "data_alert_gid": true,
2263 + "data_alert_rev": true,
2264 + "data_alert_severity": true,
2265 + "data_alert_signature": true,
2266 + "data_alert_signature_id": true,
2267 + "data_app_proto": true,
2268 + "data_audit_auid": true,
2269 + "data_audit_command": true,
2270 + "data_audit_euid": true,
2271 + "data_audit_exe": true,
2272 + "data_audit_gid": true,
2273 + "data_audit_id": true,
2274 + "data_audit_pid": true,
2275 + "data_audit_res": true,
2276 + "data_audit_session": true,
2277 + "data_audit_type": true,
2278 + "data_audit_uid": true,
2279 + "data_dest_ip": true,
2280 + "data_dest_port": true,
2281 + "data_dstuser": true,
2282 + "data_event_type": true,
2283 + "data_extra_data": true,
2284 + "data_file": true,
2285 + "data_flow_bytes_toclient": true,
2286 + "data_flow_bytes_toserver": true,
2287 + "data_flow_id": true,
2288 + "data_flow_pkts_toclient": true,
2289 + "data_flow_pkts_toserver": true,
2290 + "data_flow_start": true,
2291 + "data_http_http_content_type": true,
2292 + "data_http_http_port": true,
2293 + "data_http_length": true,
2294 + "data_http_status": true,
2295 + "data_http_url": true,
2296 + "data_id": true,
2297 + "data_in_iface": true,
2298 + "data_metadata_flowbits": true,
2299 + "data_metadata_flowints_http_anomaly_count": true,
2300 + "data_metadata_flowints_tcp_retransmission_count": true,
2301 + "data_osquery_action": true,
2302 + "data_osquery_calendarTime": true,
2303 + "data_osquery_columns_address": true,
2304 + "data_osquery_columns_address_city_name": true,
2305 + "data_osquery_columns_address_country_code": true,
2306 + "data_osquery_columns_address_geolocation": true,
2307 + "data_osquery_columns_cmdline": true,
2308 + "data_osquery_columns_cwd": true,
2309 + "data_osquery_columns_description": true,
2310 + "data_osquery_columns_directory": true,
2311 + "data_osquery_columns_disk_bytes_read": true,
2312 + "data_osquery_columns_disk_bytes_written": true,
2313 + "data_osquery_columns_egid": true,
2314 + "data_osquery_columns_euid": true,
2315 + "data_osquery_columns_family": true,
2316 + "data_osquery_columns_fd": true,
2317 + "data_osquery_columns_gid": true,
2318 + "data_osquery_columns_gid_signed": true,
2319 + "data_osquery_columns_host": true,
2320 + "data_osquery_columns_interface": true,
2321 + "data_osquery_columns_local_address": true,
2322 + "data_osquery_columns_local_address_city_name": true,
2323 + "data_osquery_columns_local_address_country_code": true,
2324 + "data_osquery_columns_local_address_geolocation": true,
2325 + "data_osquery_columns_local_port": true,
2326 + "data_osquery_columns_mac": true,
2327 + "data_osquery_columns_name": true,
2328 + "data_osquery_columns_net_namespace": true,
2329 + "data_osquery_columns_nice": true,
2330 + "data_osquery_columns_on_disk": true,
2331 + "data_osquery_columns_parent": true,
2332 + "data_osquery_columns_path": true,
2333 + "data_osquery_columns_pgroup": true,
2334 + "data_osquery_columns_pid": true,
2335 + "data_osquery_columns_port": true,
2336 + "data_osquery_columns_protocol": true,
2337 + "data_osquery_columns_remote_address": true,
2338 + "data_osquery_columns_remote_address_city_name": true,
2339 + "data_osquery_columns_remote_address_country_code": true,
2340 + "data_osquery_columns_remote_address_geolocation": true,
2341 + "data_osquery_columns_remote_port": true,
2342 + "data_osquery_columns_resident_size": true,
2343 + "data_osquery_columns_root": true,
2344 + "data_osquery_columns_sgid": true,
2345 + "data_osquery_columns_shell": true,
2346 + "data_osquery_columns_socket": true,
2347 + "data_osquery_columns_start_time": true,
2348 + "data_osquery_columns_state": true,
2349 + "data_osquery_columns_suid": true,
2350 + "data_osquery_columns_system_time": true,
2351 + "data_osquery_columns_threads": true,
2352 + "data_osquery_columns_time_utc": true,
2353 + "data_osquery_columns_total_size": true,
2354 + "data_osquery_columns_tty": true,
2355 + "data_osquery_columns_type": true,
2356 + "data_osquery_columns_uid": true,
2357 + "data_osquery_columns_uid_signed": true,
2358 + "data_osquery_columns_user": true,
2359 + "data_osquery_columns_user_time": true,
2360 + "data_osquery_columns_username": true,
2361 + "data_osquery_columns_wired_size": true,
2362 + "data_osquery_counter": true,
2363 + "data_osquery_decorations_host_uuid": true,
2364 + "data_osquery_decorations_hostname": true,
2365 + "data_osquery_epoch": true,
2366 + "data_osquery_hostIdentifier": true,
2367 + "data_osquery_name": true,
2368 + "data_osquery_numerics": true,
2369 + "data_osquery_unixTime": true,
2370 + "data_proto": true,
2371 + "data_sca_check_command": true,
2372 + "data_sca_check_compliance_cis": true,
2373 + "data_sca_check_compliance_cis_csc": true,
2374 + "data_sca_check_compliance_gdpr_IV": true,
2375 + "data_sca_check_compliance_gpg_13": true,
2376 + "data_sca_check_compliance_hipaa": true,
2377 + "data_sca_check_compliance_nist_800_53": true,
2378 + "data_sca_check_compliance_pci_dss": true,
2379 + "data_sca_check_compliance_tsc": true,
2380 + "data_sca_check_description": true,
2381 + "data_sca_check_id": true,
2382 + "data_sca_check_previous_result": true,
2383 + "data_sca_check_rationale": true,
2384 + "data_sca_check_remediation": true,
2385 + "data_sca_check_result": true,
2386 + "data_sca_check_title": true,
2387 + "data_sca_description": true,
2388 + "data_sca_failed": true,
2389 + "data_sca_file": true,
2390 + "data_sca_invalid": true,
2391 + "data_sca_passed": true,
2392 + "data_sca_policy": true,
2393 + "data_sca_policy_id": true,
2394 + "data_sca_scan_id": true,
2395 + "data_sca_score": true,
2396 + "data_sca_total_checks": true,
2397 + "data_sca_type": true,
2398 + "data_script": true,
2399 + "data_src_ip": true,
2400 + "data_src_ip_city_name": true,
2401 + "data_src_ip_country_code": true,
2402 + "data_src_ip_geolocation": true,
2403 + "data_src_port": true,
2404 + "data_srcip": true,
2405 + "data_srcip_city_name": true,
2406 + "data_srcip_country_code": true,
2407 + "data_srcip_geolocation": true,
2408 + "data_srcuser": true,
2409 + "data_timestamp": true,
2410 + "data_title": true,
2411 + "data_tls_session_resumed": true,
2412 + "data_tls_version": true,
2413 + "data_tx_id": true,
2414 + "data_type": true,
2415 + "data_win_eventXML_binaryData": true,
2416 + "data_win_eventXML_binaryDataSize": true,
2417 + "data_win_eventXML_param1": true,
2418 + "data_win_eventdata_authenticationPackageName": true,
2419 + "data_win_eventdata_callTrace": true,
2420 + "data_win_eventdata_commandLine": true,
2421 + "data_win_eventdata_company": true,
2422 + "data_win_eventdata_creationUtcTime": true,
2423 + "data_win_eventdata_currentDirectory": true,
2424 + "data_win_eventdata_description": true,
2425 + "data_win_eventdata_destinationHostname": true,
2426 + "data_win_eventdata_destinationIp": true,
2427 + "data_win_eventdata_destinationIp_city_name": true,
2428 + "data_win_eventdata_destinationIp_country_code": true,
2429 + "data_win_eventdata_destinationIp_geolocation": true,
2430 + "data_win_eventdata_destinationIsIpv6": true,
2431 + "data_win_eventdata_destinationPort": true,
2432 + "data_win_eventdata_destinationPortName": true,
2433 + "data_win_eventdata_details": true,
2434 + "data_win_eventdata_elevatedToken": true,
2435 + "data_win_eventdata_eventType": true,
2436 + "data_win_eventdata_fileVersion": true,
2437 + "data_win_eventdata_fileVersion_city_name": true,
2438 + "data_win_eventdata_fileVersion_country_code": true,
2439 + "data_win_eventdata_fileVersion_geolocation": true,
2440 + "data_win_eventdata_grantedAccess": true,
2441 + "data_win_eventdata_hashes": true,
2442 + "data_win_eventdata_image": true,
2443 + "data_win_eventdata_imageLoaded": true,
2444 + "data_win_eventdata_impersonationLevel": true,
2445 + "data_win_eventdata_initiated": true,
2446 + "data_win_eventdata_integrityLevel": true,
2447 + "data_win_eventdata_ipAddress": true,
2448 + "data_win_eventdata_ipPort": true,
2449 + "data_win_eventdata_keyLength": true,
2450 + "data_win_eventdata_logonGuid": true,
2451 + "data_win_eventdata_logonId": true,
2452 + "data_win_eventdata_logonProcessName": true,
2453 + "data_win_eventdata_logonType": true,
2454 + "data_win_eventdata_originalFileName": true,
2455 + "data_win_eventdata_param1": true,
2456 + "data_win_eventdata_param2": true,
2457 + "data_win_eventdata_param3": true,
2458 + "data_win_eventdata_param4": true,
2459 + "data_win_eventdata_parentCommandLine": true,
2460 + "data_win_eventdata_parentImage": true,
2461 + "data_win_eventdata_parentProcessGuid": true,
2462 + "data_win_eventdata_parentProcessId": true,
2463 + "data_win_eventdata_processGuid": true,
2464 + "data_win_eventdata_processId": true,
2465 + "data_win_eventdata_processName": true,
2466 + "data_win_eventdata_product": true,
2467 + "data_win_eventdata_protocol": true,
2468 + "data_win_eventdata_queryName": true,
2469 + "data_win_eventdata_queryResults": true,
2470 + "data_win_eventdata_queryStatus": true,
2471 + "data_win_eventdata_ruleName": true,
2472 + "data_win_eventdata_serviceName": true,
2473 + "data_win_eventdata_serviceSid": true,
2474 + "data_win_eventdata_signature": true,
2475 + "data_win_eventdata_signatureStatus": true,
2476 + "data_win_eventdata_signed": true,
2477 + "data_win_eventdata_sourceHostname": true,
2478 + "data_win_eventdata_sourceImage": true,
2479 + "data_win_eventdata_sourceIp": true,
2480 + "data_win_eventdata_sourceIp_city_name": true,
2481 + "data_win_eventdata_sourceIp_country_code": true,
2482 + "data_win_eventdata_sourceIp_geolocation": true,
2483 + "data_win_eventdata_sourceIsIpv6": true,
2484 + "data_win_eventdata_sourcePort": true,
2485 + "data_win_eventdata_sourceProcessGUID": true,
2486 + "data_win_eventdata_sourceProcessId": true,
2487 + "data_win_eventdata_sourceThreadId": true,
2488 + "data_win_eventdata_status": true,
2489 + "data_win_eventdata_subjectDomainName": true,
2490 + "data_win_eventdata_subjectLogonId": true,
2491 + "data_win_eventdata_subjectUserName": true,
2492 + "data_win_eventdata_subjectUserSid": true,
2493 + "data_win_eventdata_targetDomainName": true,
2494 + "data_win_eventdata_targetFilename": true,
2495 + "data_win_eventdata_targetImage": true,
2496 + "data_win_eventdata_targetLinkedLogonId": true,
2497 + "data_win_eventdata_targetLogonId": true,
2498 + "data_win_eventdata_targetObject": true,
2499 + "data_win_eventdata_targetProcessGUID": true,
2500 + "data_win_eventdata_targetProcessId": true,
2501 + "data_win_eventdata_targetUserName": true,
2502 + "data_win_eventdata_targetUserSid": true,
2503 + "data_win_eventdata_terminalSessionId": true,
2504 + "data_win_eventdata_ticketEncryptionType": true,
2505 + "data_win_eventdata_ticketOptions": true,
2506 + "data_win_eventdata_user": true,
2507 + "data_win_eventdata_utcTime": true,
2508 + "data_win_eventdata_virtualAccount": true,
2509 + "data_win_system_channel": true,
2510 + "data_win_system_computer": true,
2511 + "data_win_system_eventID": true,
2512 + "data_win_system_eventRecordID": true,
2513 + "data_win_system_eventSourceName": true,
2514 + "data_win_system_keywords": true,
2515 + "data_win_system_level": true,
2516 + "data_win_system_message": true,
2517 + "data_win_system_opcode": true,
2518 + "data_win_system_processID": true,
2519 + "data_win_system_providerGuid": true,
2520 + "data_win_system_providerName": true,
2521 + "data_win_system_severityValue": true,
2522 + "data_win_system_systemTime": true,
2523 + "data_win_system_task": true,
2524 + "data_win_system_threadID": true,
2525 + "data_win_system_version": true,
2526 + "decoder_name": true,
2527 + "decoder_parent": true,
2528 + "deviceExternalId": true,
2529 + "device_product": true,
2530 + "device_vendor": true,
2531 + "device_version": true,
2532 + "dns_query": true,
2533 + "dns_query_threat_indicated": true,
2534 + "dst_ip": true,
2535 + "dst_ip_city_name": true,
2536 + "dst_ip_country_code": true,
2537 + "dst_ip_geolocation": true,
2538 + "dst_ip_threat_indicated": true,
2539 + "dst_port": true,
2540 + "dvc": true,
2541 + "dvchost": true,
2542 + "ecs_version": true,
2543 + "error": true,
2544 + "event_class_id": true,
2545 + "event_hash": true,
2546 + "facility": true,
2547 + "fileHash": true,
2548 + "filePath": true,
2549 + "file_path": true,
2550 + "firewall_rule_name": true,
2551 + "fname": true,
2552 + "full_log": false,
2553 + "gl2_accounted_message_size": true,
2554 + "gl2_message_id": true,
2555 + "gl2_remote_ip": true,
2556 + "gl2_remote_port": true,
2557 + "gl2_source_collector": true,
2558 + "gl2_source_input": true,
2559 + "gl2_source_node": true,
2560 + "hash_md5": true,
2561 + "hash_sha1": true,
2562 + "hash_sha256": true,
2563 + "highlight": true,
2564 + "host_architecture": true,
2565 + "host_containerized": true,
2566 + "host_hostname": true,
2567 + "host_id": true,
2568 + "host_ip": true,
2569 + "host_mac": true,
2570 + "host_name": true,
2571 + "host_os_codename": true,
2572 + "host_os_kernel": true,
2573 + "host_os_name": true,
2574 + "host_os_platform": true,
2575 + "host_os_version": true,
2576 + "hostname": true,
2577 + "id": true,
2578 + "input_type": true,
2579 + "level": true,
2580 + "location": true,
2581 + "log_file_path": true,
2582 + "log_offset": true,
2583 + "manager_name": true,
2584 + "message": true,
2585 + "module": true,
2586 + "name": true,
2587 + "parent_process_cmd_line": true,
2588 + "parent_process_id": true,
2589 + "parent_process_image": true,
2590 + "pid": true,
2591 + "predecoder_hostname": true,
2592 + "predecoder_program_name": true,
2593 + "predecoder_timestamp": true,
2594 + "previous_log": true,
2595 + "previous_output": true,
2596 + "process_cmd_line": true,
2597 + "process_id": true,
2598 + "process_image": true,
2599 + "process_name": true,
2600 + "protocol": true,
2601 + "rule_cis": true,
2602 + "rule_cis_csc": true,
2603 + "rule_firedtimes": true,
2604 + "rule_gdpr": true,
2605 + "rule_gdpr_IV": true,
2606 + "rule_gpg13": true,
2607 + "rule_gpg_13": true,
2608 + "rule_hipaa": true,
2609 + "rule_id": false,
2610 + "rule_info": true,
2611 + "rule_mail": true,
2612 + "rule_mitre_id": true,
2613 + "rule_mitre_tactic": false,
2614 + "rule_nist_800_53": true,
2615 + "rule_pci_dss": true,
2616 + "rule_tsc": true,
2617 + "scanid": true,
2618 + "service": true,
2619 + "severity": true,
2620 + "software_package": true,
2621 + "software_vendor": true,
2622 + "sort": true,
2623 + "source": true,
2624 + "spid": true,
2625 + "sproc": true,
2626 + "src_ip": true,
2627 + "src_ip_city_name": true,
2628 + "src_ip_country_code": true,
2629 + "src_ip_geolocation": true,
2630 + "src_port": true,
2631 + "start": true,
2632 + "streams": true,
2633 + "suid": true,
2634 + "syscheck_attrs_after": true,
2635 + "syscheck_audit_effective_user_id": true,
2636 + "syscheck_audit_effective_user_name": true,
2637 + "syscheck_audit_group_id": true,
2638 + "syscheck_audit_group_name": true,
2639 + "syscheck_audit_login_user_id": true,
2640 + "syscheck_audit_login_user_name": true,
2641 + "syscheck_audit_process_cwd": true,
2642 + "syscheck_audit_process_id": true,
2643 + "syscheck_audit_process_name": true,
2644 + "syscheck_audit_process_parent_cwd": true,
2645 + "syscheck_audit_process_parent_name": true,
2646 + "syscheck_audit_process_ppid": true,
2647 + "syscheck_audit_user_id": true,
2648 + "syscheck_audit_user_name": true,
2649 + "syscheck_changed_attributes": true,
2650 + "syscheck_event": true,
2651 + "syscheck_gid_after": true,
2652 + "syscheck_gname_after": true,
2653 + "syscheck_hard_links": true,
2654 + "syscheck_inode_after": true,
2655 + "syscheck_inode_before": true,
2656 + "syscheck_md5_after": true,
2657 + "syscheck_md5_before": true,
2658 + "syscheck_mode": true,
2659 + "syscheck_mtime_after": true,
2660 + "syscheck_mtime_before": true,
2661 + "syscheck_path": true,
2662 + "syscheck_perm_after": true,
2663 + "syscheck_perm_before": true,
2664 + "syscheck_sha1_after": true,
2665 + "syscheck_sha1_before": true,
2666 + "syscheck_sha256_after": true,
2667 + "syscheck_sha256_before": true,
2668 + "syscheck_size_after": true,
2669 + "syscheck_size_before": true,
2670 + "syscheck_uid_after": true,
2671 + "syscheck_uname_after": true,
2672 + "syscheck_win_perm_after": true,
2673 + "syscheck_win_perm_after_0_allowed": true,
2674 + "syscheck_win_perm_after_0_name": true,
2675 + "syscheck_win_perm_after_1_allowed": true,
2676 + "syscheck_win_perm_after_1_name": true,
2677 + "syscheck_win_perm_after_2_allowed": true,
2678 + "syscheck_win_perm_after_2_name": true,
2679 + "syscheck_win_perm_after_3_allowed": true,
2680 + "syscheck_win_perm_after_3_name": true,
2681 + "syslog_customer": true,
2682 + "syslog_tag": true,
2683 + "syslog_type": true,
2684 + "sysmon_event_description": true,
2685 + "threat_ids": true,
2686 + "threat_indicated": true,
2687 + "threat_names": true,
2688 + "time": true,
2689 + "timestamp": false,
2690 + "user_name": true,
2691 + "win_registry_key": true,
2692 + "win_system_eventID": true,
2693 + "windows_auth_package": true,
2694 + "windows_domain": true,
2695 + "windows_event_id": true,
2696 + "windows_event_severity": true,
2697 + "windows_logon_type": true
2698 + },
2699 + "indexByName": {
2700 + "BitdefenderGZAttCkId": 4,
2701 + "BitdefenderGZAttackEntry": 5,
2702 + "BitdefenderGZAttackTypes": 6,
2703 + "BitdefenderGZCompanyId": 8,
2704 + "BitdefenderGZComputerFQDN": 3,
2705 + "BitdefenderGZDetectionName": 9,
2706 + "BitdefenderGZDetectionTime": 10,
2707 + "BitdefenderGZEndpointId": 11,
2708 + "BitdefenderGZFileHashSha256": 12,
2709 + "BitdefenderGZIncidentId": 13,
2710 + "BitdefenderGZMainAction": 14,
2711 + "BitdefenderGZMalwareHash": 15,
2712 + "BitdefenderGZMalwareName": 16,
2713 + "BitdefenderGZMalwareType": 17,
2714 + "BitdefenderGZModule": 7,
2715 + "BitdefenderGZParentPath": 18,
2716 + "BitdefenderGZParentPid": 19,
2717 + "BitdefenderGZProcessCommandLine": 20,
2718 + "BitdefenderGZScanEngineType": 21,
2719 + "BitdefenderGZSeverityScore": 22,
2720 + "BitdefenderGZSignaturesNumber": 23,
2721 + "_id": 1,
2722 + "_index": 24,
2723 + "_type": 25,
2724 + "act": 26,
2725 + "deviceExternalId": 27,
2726 + "device_product": 28,
2727 + "device_vendor": 29,
2728 + "device_version": 30,
2729 + "dvc": 31,
2730 + "dvchost": 32,
2731 + "event_class_id": 33,
2732 + "facility": 34,
2733 + "fileHash": 35,
2734 + "filePath": 36,
2735 + "fname": 37,
2736 + "gl2_accounted_message_size": 38,
2737 + "gl2_message_id": 39,
2738 + "gl2_remote_ip": 40,
2739 + "gl2_remote_port": 41,
2740 + "gl2_source_input": 42,
2741 + "gl2_source_node": 43,
2742 + "highlight": 44,
2743 + "level": 2,
2744 + "message": 45,
2745 + "name": 46,
2746 + "severity": 47,
2747 + "sort": 48,
2748 + "source": 49,
2749 + "spid": 50,
2750 + "sproc": 51,
2751 + "start": 52,
2752 + "streams": 53,
2753 + "suid": 54,
2754 + "suser": 55,
2755 + "syslog_type": 56,
2756 + "timestamp": 0
2757 + },
2758 + "renameByName": {
2759 + "BitdefenderGZAttackEntry": "ATTACK",
2760 + "BitdefenderGZAttackTypes": "ATTACK TYPE",
2761 + "BitdefenderGZCompanyId": "",
2762 + "BitdefenderGZComputerFQDN": "AGENT",
2763 + "BitdefenderGZDetectionName": "DETECTION",
2764 + "BitdefenderGZMainAction": "ACTION",
2765 + "BitdefenderGZModule": "MODULE",
2766 + "_id": "EVENT ID",
2767 + "rule_id": "RULE ID",
2768 + "rule_mitre_tactic": "MITRE TACTIC",
2769 + "rule_mitre_technique": "MITRE TECHNIQUE",
2770 + "suser": "USER",
2771 + "timestamp": ""
2772 + }
2773 + }
2774 + }
2775 + ],
2776 + "transparent": true,
2777 + "type": "table"
2778 + }
2779 + ],
2780 + "refresh": false,
2781 + "schemaVersion": 37,
2782 + "style": "dark",
2783 + "tags": ["EDR"],
2784 + "templating": {
2785 + "list": [
2786 + {
2787 + "datasource": {
2788 + "type": "grafana-opensearch-datasource",
2789 + "uid": "replace_datasource_uid"
2790 + },
2791 + "filters": [],
2792 + "hide": 0,
2793 + "label": "",
2794 + "name": "Filters",
2795 + "skipUrlSync": false,
2796 + "type": "adhoc"
2797 + },
2798 + {
2799 + "current": {
2800 + "selected": false,
2801 + "text": "All",
2802 + "value": "$__all"
2803 + },
2804 + "datasource": {
2805 + "type": "grafana-opensearch-datasource",
2806 + "uid": "replace_datasource_uid"
2807 + },
2808 + "definition": "{ \"find\": \"terms\", \"field\": \"BitdefenderGZComputerFQDN\", \"query\": \"\"}",
2809 + "hide": 0,
2810 + "includeAll": true,
2811 + "label": "Agent",
2812 + "multi": false,
2813 + "name": "agent_name",
2814 + "options": [],
2815 + "query": "{ \"find\": \"terms\", \"field\": \"BitdefenderGZComputerFQDN\", \"query\": \"\"}",
2816 + "refresh": 2,
2817 + "regex": "",
2818 + "skipUrlSync": false,
2819 + "sort": 2,
2820 + "tagValuesQuery": "",
2821 + "tagsQuery": "",
2822 + "type": "query",
2823 + "useTags": false
2824 + },
2825 + {
2826 + "current": {
2827 + "selected": false,
2828 + "text": "All",
2829 + "value": "$__all"
2830 + },
2831 + "datasource": {
2832 + "type": "grafana-opensearch-datasource",
2833 + "uid": "replace_datasource_uid"
2834 + },
2835 + "definition": "{ \"find\": \"terms\", \"field\": \"level\", \"query\": \"\"}",
2836 + "hide": 0,
2837 + "includeAll": true,
2838 + "label": "Level",
2839 + "multi": false,
2840 + "name": "level",
2841 + "options": [],
2842 + "query": "{ \"find\": \"terms\", \"field\": \"level\", \"query\": \"\"}",
2843 + "refresh": 2,
2844 + "regex": "",
2845 + "skipUrlSync": false,
2846 + "sort": 0,
2847 + "type": "query"
2848 + }
2849 + ]
2850 + },
2851 + "time": {
2852 + "from": "now-7d",
2853 + "to": "now"
2854 + },
2855 + "timepicker": {
2856 + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"],
2857 + "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"]
2858 + },
2859 + "timezone": "",
2860 + "title": "EDR - BITDEFENDER EPP",
2861 + "version": 3,
2862 + "weekStart": ""
2863 +}
backend/app/connectors/grafana/schema/dashboards.py
+5
@@ -106,6 +106,10 @@ class DarktraceDashboard(Enum):
106 DARKTRACE_SUMMARY = ("Darktrace", "summary.json")
107
108
109 +class BitdefenderDashboard(Enum):
110 + BITDEFENDER_SUMMARY = ("Bitdefender", "summary.json")
111 +
112 +
113 class DashboardProvisionRequest(BaseModel):
114 dashboards: List[str] = Field(
115 ...,
@@ -139,6 +143,7 @@ class DashboardProvisionRequest(BaseModel):
143 + list(CrowdstrikeDashboard)
144 + list(DuoDashboard)
145 + list(DarktraceDashboard)
146 + + list(BitdefenderDashboard)
147 }
148 if e not in valid_dashboards:
149 raise ValueError(f'Dashboard identifier "{e}" is not recognized.')
backend/app/connectors/grafana/services/dashboards.py
+2
@@ -4,6 +4,7 @@ from pathlib import Path
4 from fastapi import HTTPException
5 from loguru import logger
6
7 +from app.connectors.grafana.schema.dashboards import BitdefenderDashboard
8 from app.connectors.grafana.schema.dashboards import CarbonBlackDashboard
9 from app.connectors.grafana.schema.dashboards import CrowdstrikeDashboard
10 from app.connectors.grafana.schema.dashboards import DarktraceDashboard
@@ -185,6 +186,7 @@ async def provision_dashboards(
186 + list(CrowdstrikeDashboard)
187 + list(DuoDashboard)
188 + list(DarktraceDashboard)
189 + + list(BitdefenderDashboard)
190 }
191
192 for dashboard_name in dashboard_request.dashboards:
backend/app/db/db_populate.py
+18 -2
@@ -303,7 +303,7 @@ def load_markdown_for_integration(integration_name: str) -> str:
303 with open(file_path, "r") as file:
304 return file.read()
305 except FileNotFoundError:
306 - return "No deployment intrusctions available."
306 + return "No deployment instructions available."
307
308
309 def get_available_integrations_list():
@@ -322,6 +322,7 @@ def get_available_integrations_list():
322 ("Crowdstrike", "Integrate Crowdstrike with SOCFortress."),
323 ("DUO", "Integrate DUO with SOCFortress."),
324 ("Darktrace", "Integrate Darktrace with SOCFortress."),
325 + ("BitDefender", "Integrate BitDefender with SOCFortress."),
326 # ... Add more available integrations as needed ...
327 ]
328
@@ -364,6 +365,15 @@ async def add_available_integrations_if_not_exist(session: AsyncSession):
365 logger.info(
366 f"Added new available integration: {available_integration_data['integration_name']}",
367 )
368 + else:
369 + # Check if the integration details need to be updated
370 + if existing_available_integration.integration_details == "No deployment instructions available.":
371 + new_integration_details = load_markdown_for_integration(available_integration_data["integration_name"])
372 + if new_integration_details != "No deployment instructions available.":
373 + existing_available_integration.integration_details = new_integration_details
374 + logger.info(
375 + f"Updated integration details for {available_integration_data['integration_name']}",
376 + )
377 except Exception as e:
378 logger.error(f"Error adding available integration: {e}")
379 await session.rollback()
@@ -441,6 +451,12 @@ async def get_available_integrations_auth_keys_list(session: AsyncSession):
451 ("Darktrace", "PRIVATE_TOKEN"),
452 ("Darktrace", "HOST"),
453 ("Darktrace", "PORT"),
454 + ("BitDefender", "BASIC_AUTH_USERNAME"),
455 + ("BitDefender", "BASIC_AUTH_PASSWORD"),
456 + ("BitDefender", "WEBSERVER_HOSTNAME"),
457 + ("BitDefender", "WEBSERVER_PORT"),
458 + ("BitDefender", "GRAYLOG_PORT"),
459 + ("BitDefender", "API_KEY"),
460 # ... Add more available integrations auth keys as needed ...
461 ]
462 logger.info("Getting available integrations auth keys.")
@@ -565,7 +581,7 @@ def load_markdown_for_network_connector(network_connector_name: str) -> str:
581 with open(file_path, "r") as file:
582 return file.read()
583 except FileNotFoundError:
568 - return "No deployment intrusctions available."
584 + return "No deployment instructions available."
585
586
587 def get_available_network_connectors_list():
backend/app/integrations/bitdefender/routes/provision.py new
+127
@@ -0,0 +1,127 @@
1 +from typing import Dict
2 +
3 +from fastapi import APIRouter
4 +from fastapi import Depends
5 +from fastapi import HTTPException
6 +from fastapi import Security
7 +from sqlalchemy.ext.asyncio import AsyncSession
8 +
9 +from app.auth.utils import AuthHandler
10 +from app.db.db_session import get_db
11 +from app.integrations.bitdefender.schema.provision import BitdefenderCustomerDetails
12 +from app.integrations.bitdefender.schema.provision import ProvisionBitdefenderAuthKeys
13 +from app.integrations.bitdefender.schema.provision import ProvisionBitdefenderRequest
14 +from app.integrations.bitdefender.schema.provision import ProvisionBitdefenderResponse
15 +from app.integrations.bitdefender.services.provision import provision_bitdefender
16 +from app.integrations.routes import find_customer_integration
17 +from app.integrations.routes import get_customer_integrations_by_customer_code
18 +from app.integrations.schema import CustomerIntegrations
19 +from app.integrations.schema import CustomerIntegrationsResponse
20 +
21 +integration_bitdefender_router = APIRouter()
22 +
23 +
24 +async def get_customer_integration_response(
25 + customer_code: str,
26 + session: AsyncSession,
27 +) -> CustomerIntegrationsResponse:
28 + """
29 + Retrieves the integration response for a customer.
30 +
31 + Args:
32 + customer_code (str): The code of the customer.
33 + session (AsyncSession): The async session object for database operations.
34 +
35 + Returns:
36 + CustomerIntegrationsResponse: The integration response for the customer.
37 +
38 + Raises:
39 + HTTPException: If the customer integration settings are not found.
40 + """
41 + customer_integration_response = await get_customer_integrations_by_customer_code(
42 + customer_code,
43 + session,
44 + )
45 + if customer_integration_response.available_integrations == []:
46 + raise HTTPException(
47 + status_code=404,
48 + detail="Customer integration settings not found.",
49 + )
50 + return customer_integration_response
51 +
52 +
53 +def extract_bitdefender_auth_keys(
54 + customer_integration: CustomerIntegrations,
55 +) -> Dict[str, str]:
56 + """
57 + Extracts the authentication keys for Bitdefender integration from the given customer integration.
58 +
59 + Args:
60 + customer_integration (CustomerIntegrations): The customer integration object.
61 +
62 + Returns:
63 + Dict[str, str]: A dictionary containing the authentication keys for Bitdefender integration.
64 +
65 + Raises:
66 + HTTPException: If no authentication keys are found for Bitdefender integration.
67 + """
68 + bitdefender_auth_keys = {}
69 + for subscription in customer_integration.integration_subscriptions:
70 + if subscription.integration_service.service_name == "BitDefender":
71 + for auth_key in subscription.integration_auth_keys:
72 + bitdefender_auth_keys[auth_key.auth_key_name] = auth_key.auth_value
73 + if not bitdefender_auth_keys:
74 + raise HTTPException(
75 + status_code=404,
76 + detail="No auth keys found for Bitdefender integration. Please create auth keys for Bitdefender integration.",
77 + )
78 + return bitdefender_auth_keys
79 +
80 +
81 +@integration_bitdefender_router.post(
82 + "/provision",
83 + response_model=ProvisionBitdefenderResponse,
84 + description="Provision Bitdefender integration for a customer.",
85 + dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
86 +)
87 +async def provision_bitdefender_route(
88 + provision_bitdefender_request: ProvisionBitdefenderRequest,
89 + session: AsyncSession = Depends(get_db),
90 +) -> ProvisionBitdefenderResponse:
91 + """
92 + Provisions Bitdefender integration for a customer.
93 +
94 + Args:
95 + provision_bitdefender_request (ProvisionBitdefenderRequest): The request object containing the necessary information for provisioning.
96 + session (AsyncSession, optional): The database session. Defaults to Depends(get_db).
97 +
98 + Returns:
99 + ProvisionBitdefenderResponse: The response object containing the result of the provisioning.
100 + """
101 + customer_integration_response = await get_customer_integration_response(
102 + provision_bitdefender_request.customer_code,
103 + session,
104 + )
105 +
106 + customer_integration = await find_customer_integration(
107 + provision_bitdefender_request.customer_code,
108 + provision_bitdefender_request.integration_name,
109 + customer_integration_response,
110 + )
111 +
112 + bitdefender_auth_keys = extract_bitdefender_auth_keys(customer_integration)
113 +
114 + auth_keys = ProvisionBitdefenderAuthKeys(**bitdefender_auth_keys)
115 +
116 + return await provision_bitdefender(
117 + customer_details=BitdefenderCustomerDetails(
118 + customer_code=provision_bitdefender_request.customer_code,
119 + customer_name=customer_integration.customer_name,
120 + protocal_type="Tcp",
121 + syslog_port=int(auth_keys.GRAYLOG_PORT),
122 + hot_data_retention=provision_bitdefender_request.hot_data_retention,
123 + index_replicas=provision_bitdefender_request.index_replicas,
124 + ),
125 + keys=auth_keys,
126 + session=session,
127 + )
backend/app/integrations/bitdefender/schema/provision.py new
+107
@@ -0,0 +1,107 @@
1 +from typing import Any
2 +from typing import Dict
3 +from typing import Optional
4 +
5 +from pydantic import BaseModel
6 +from pydantic import Field
7 +from pydantic import root_validator
8 +
9 +
10 +class ProvisionBitdefenderRequest(BaseModel):
11 + customer_code: str = Field(
12 + ...,
13 + description="The customer code.",
14 + examples=["00001"],
15 + )
16 + integration_name: str = Field(
17 + "Bitdefender",
18 + description="The integration name.",
19 + examples=["BitDefender"],
20 + )
21 + hot_data_retention: Optional[int] = Field(
22 + 30,
23 + example=30,
24 + description="Number of days to retain hot data",
25 + )
26 + index_replicas: Optional[int] = Field(
27 + 0,
28 + example=1,
29 + description="Number of replicas for the customer's Graylog instance",
30 + )
31 +
32 + # ensure the `integration_name` is always set to "Bitdefender"
33 + @root_validator(pre=True)
34 + def set_integration_name(cls, values: Dict[str, Any]) -> Dict[str, Any]:
35 + values["integration_name"] = "BitDefender"
36 + return values
37 +
38 +
39 +class ProvisionBitdefenderResponse(BaseModel):
40 + success: bool
41 + message: str
42 +
43 +
44 +class ProvisionBitdefenderAuthKeys(BaseModel):
45 + BASIC_AUTH_USERNAME: str = Field(
46 + ...,
47 + description="The basic auth username.",
48 + examples=["admin"],
49 + )
50 + BASIC_AUTH_PASSWORD: str = Field(
51 + ...,
52 + description="The basic auth password.",
53 + examples=["password"],
54 + )
55 + WEBSERVER_HOSTNAME: str = Field(
56 + ...,
57 + description="The webserver hostname.",
58 + examples=["firehose.socfortress.co"],
59 + )
60 + WEBSERVER_PORT: str = Field(
61 + ...,
62 + description="The webserver port.",
63 + examples=["3200"],
64 + )
65 + GRAYLOG_PORT: str = Field(
66 + ...,
67 + description="The Graylog port.",
68 + examples=["10514"],
69 + )
70 + API_KEY: str = Field(
71 + ...,
72 + description="The API key.",
73 + examples=["api_key"],
74 + )
75 +
76 +
77 +class BitdefenderCustomerDetails(BaseModel):
78 + customer_name: str = Field(
79 + ...,
80 + description="The customer name.",
81 + examples=["Customer 1"],
82 + )
83 + customer_code: str = Field(
84 + ...,
85 + description="The customer code.",
86 + examples=["00002"],
87 + )
88 + protocal_type: str = Field(
89 + ...,
90 + description="The protocal type.",
91 + examples=["TCP"],
92 + )
93 + syslog_port: int = Field(
94 + ...,
95 + description="The syslog port.",
96 + examples=[514],
97 + )
98 + hot_data_retention: int = Field(
99 + ...,
100 + example=30,
101 + description="Number of days to retain hot data",
102 + )
103 + index_replicas: int = Field(
104 + ...,
105 + example=1,
106 + description="Number of replicas for the customer's Graylog instance",
107 + )
backend/app/integrations/bitdefender/services/provision.py new
+646
@@ -0,0 +1,646 @@
1 +import base64
2 +import json
3 +import os
4 +from datetime import datetime
5 +
6 +import aiofiles
7 +import httpx
8 +from fastapi import HTTPException
9 +from loguru import logger
10 +from sqlalchemy import and_
11 +from sqlalchemy import update
12 +from sqlalchemy.ext.asyncio import AsyncSession
13 +
14 +from app.connectors.grafana.schema.dashboards import BitdefenderDashboard
15 +from app.connectors.grafana.schema.dashboards import DashboardProvisionRequest
16 +from app.connectors.grafana.services.dashboards import provision_dashboards
17 +from app.connectors.grafana.utils.universal import create_grafana_client
18 +from app.connectors.graylog.services.collector import (
19 + get_content_pack_id_by_content_pack_name,
20 +)
21 +from app.connectors.graylog.services.collector import get_input_id_by_input_name
22 +from app.connectors.graylog.services.collector import get_stream_id_by_stream_name
23 +from app.connectors.graylog.services.streams import assign_stream_to_index
24 +from app.connectors.graylog.utils.universal import send_post_request
25 +from app.connectors.wazuh_indexer.services.monitoring import (
26 + output_shard_number_to_be_set_based_on_nodes,
27 +)
28 +from app.customer_provisioning.schema.grafana import GrafanaDatasource
29 +from app.customer_provisioning.schema.grafana import GrafanaDataSourceCreationResponse
30 +from app.customer_provisioning.schema.graylog import GraylogIndexSetCreationResponse
31 +from app.customer_provisioning.schema.graylog import TimeBasedIndexSet
32 +from app.customer_provisioning.schema.provision import ProvisionNewCustomer
33 +from app.customer_provisioning.services.grafana import create_grafana_folder
34 +from app.customer_provisioning.services.grafana import get_opensearch_version
35 +from app.customers.routes.customers import get_customer_meta
36 +from app.integrations.bitdefender.schema.provision import BitdefenderCustomerDetails
37 +from app.integrations.bitdefender.schema.provision import ProvisionBitdefenderAuthKeys
38 +from app.integrations.bitdefender.schema.provision import ProvisionBitdefenderResponse
39 +from app.integrations.models.customer_integration_settings import CustomerIntegrations
40 +from app.network_connectors.models.network_connectors import (
41 + CustomerNetworkConnectorsMeta,
42 +)
43 +from app.stack_provisioning.graylog.schema.provision import ContentPackKeywords
44 +from app.stack_provisioning.graylog.schema.provision import (
45 + ProvisionNetworkContentPackRequest,
46 +)
47 +from app.stack_provisioning.graylog.services.provision import (
48 + provision_content_pack_network_connector,
49 +)
50 +from app.utils import get_connector_attribute
51 +from app.utils import get_customer_meta_attribute
52 +
53 +
54 +async def base64_encode(api_key: str):
55 + """
56 + Base64 encode the given API key.
57 +
58 + Args:
59 + api_key (str): The API key to encode.
60 +
61 + Returns:
62 + str: The base64 encoded API key.
63 + """
64 + # Add a : at the end then encode (Required for Basic Auth)
65 + return base64.b64encode(f"{api_key}:".encode()).decode()
66 +
67 +
68 +async def send_configuration_to_bitdefender(keys: ProvisionBitdefenderAuthKeys):
69 + url = "https://cloud.gravityzone.bitdefender.com/api/v1.0/jsonrpc/push"
70 + headers = {
71 + "authorization": f"Basic {await base64_encode(keys.API_KEY)}",
72 + "cache-control": "no-cache",
73 + "content-type": "application/json",
74 + }
75 + data = {
76 + "id": "1",
77 + "jsonrpc": "2.0",
78 + "method": "setPushEventSettings",
79 + "params": {
80 + "serviceSettings": {
81 + "requireValidSslCertificate": False,
82 + "authorization": await base64_auth_header_generator(keys.BASIC_AUTH_USERNAME, keys.BASIC_AUTH_PASSWORD),
83 + "url": f"https://{keys.WEBSERVER_HOSTNAME}:{keys.WEBSERVER_PORT}/api",
84 + },
85 + "serviceType": "cef",
86 + "status": 1,
87 + "subscribeToEventTypes": {
88 + "adcloudgz": True,
89 + "antiexploit": True,
90 + "aph": True,
91 + "av": True,
92 + "avc": True,
93 + "dp": True,
94 + "endpoint-moved-in": True,
95 + "endpoint-moved-out": True,
96 + "exchange-malware": True,
97 + "exchange-user-credentials": True,
98 + "fw": True,
99 + "hd": True,
100 + "hwid-change": True,
101 + "install": True,
102 + "modules": True,
103 + "network-monitor": True,
104 + "network-sandboxing": True,
105 + "new-incident": True,
106 + "ransomware-mitigation": True,
107 + "registration": True,
108 + "supa-update-status": True,
109 + "sva": True,
110 + "sva-load": True,
111 + "task-status": True,
112 + "troubleshooting-activity": True,
113 + "uc": True,
114 + "uninstall": True,
115 + },
116 + },
117 + }
118 +
119 + async with httpx.AsyncClient(verify=False) as client:
120 + response = await client.post(url, headers=headers, json=data)
121 + return response.json()
122 +
123 +
124 +#### ! GRAYLOG ! ####
125 +async def build_index_set_config(request: BitdefenderCustomerDetails) -> TimeBasedIndexSet:
126 + """
127 + Build the configuration for a time-based index set.
128 +
129 + Args:
130 + request (BitdefenderCustomerDetails): The request object containing customer information.
131 +
132 + Returns:
133 + TimeBasedIndexSet: The configured time-based index set.
134 + """
135 + return TimeBasedIndexSet(
136 + title=f"{request.customer_name} - BITDEFENDER EVENTS",
137 + description=f"{request.customer_name} - BITDEFENDER EVENTS",
138 + index_prefix=f"bitdefender-{request.customer_code}",
139 + rotation_strategy_class="org.graylog2.indexer.rotation.strategies.TimeBasedRotationStrategy",
140 + rotation_strategy={
141 + "type": "org.graylog2.indexer.rotation.strategies.TimeBasedRotationStrategyConfig",
142 + "rotation_period": "P1D",
143 + "rotate_empty_index_set": False,
144 + "max_rotation_period": None,
145 + },
146 + retention_strategy_class="org.graylog2.indexer.retention.strategies.DeletionRetentionStrategy",
147 + retention_strategy={
148 + "type": "org.graylog2.indexer.retention.strategies.DeletionRetentionStrategyConfig",
149 + "max_number_of_indices": request.hot_data_retention,
150 + },
151 + creation_date=datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
152 + index_analyzer="standard",
153 + shards=await output_shard_number_to_be_set_based_on_nodes(),
154 + replicas=request.index_replicas,
155 + index_optimization_max_num_segments=1,
156 + index_optimization_disabled=False,
157 + writable=True,
158 + field_type_refresh_interval=5000,
159 + )
160 +
161 +
162 +# Function to send the POST request and handle the response
163 +async def send_index_set_creation_request(
164 + index_set: TimeBasedIndexSet,
165 +) -> GraylogIndexSetCreationResponse:
166 + """
167 + Sends a request to create an index set in Graylog.
168 +
169 + Args:
170 + index_set (TimeBasedIndexSet): The index set to be created.
171 +
172 + Returns:
173 + GraylogIndexSetCreationResponse: The response from Graylog after creating the index set.
174 + """
175 + json_index_set = json.dumps(index_set.dict())
176 + logger.info(f"json_index_set set: {json_index_set}")
177 + response_json = await send_post_request(
178 + endpoint="/api/system/indices/index_sets",
179 + data=index_set.dict(),
180 + )
181 + return GraylogIndexSetCreationResponse(**response_json)
182 +
183 +
184 +# Refactored create_index_set function
185 +async def create_index_set(
186 + request: ProvisionNewCustomer,
187 +) -> GraylogIndexSetCreationResponse:
188 + """
189 + Creates an index set for a new customer.
190 +
191 + Args:
192 + request (ProvisionNewCustomer): The request object containing the customer information.
193 +
194 + Returns:
195 + GraylogIndexSetCreationResponse: The response object containing the result of the index set creation.
196 + """
197 + logger.info(f"Creating index set for customer {request.customer_name}")
198 + index_set_config = await build_index_set_config(request)
199 + return await send_index_set_creation_request(index_set_config)
200 +
201 +
202 +async def provision_content_pack(customer_details):
203 + """
204 + Provisions a content pack for a customer.
205 +
206 + Args:
207 + customer_details (CustomerDetails): The details of the customer.
208 +
209 + Returns:
210 + ContentPack: The provisioned content pack.
211 + """
212 + return await provision_content_pack_network_connector(
213 + content_pack_request=ProvisionNetworkContentPackRequest(
214 + content_pack_name="BITDEFENDER",
215 + keywords=ContentPackKeywords(
216 + customer_name=customer_details.customer_name,
217 + customer_code=customer_details.customer_code,
218 + protocol_type=customer_details.protocal_type,
219 + syslog_port=customer_details.syslog_port,
220 + ),
221 + ),
222 + )
223 +
224 +
225 +async def get_stream_and_index_ids(customer_details):
226 + """
227 + Retrieves the stream ID and index ID for a given customer.
228 +
229 + Args:
230 + customer_details (CustomerDetails): The details of the customer.
231 +
232 + Returns:
233 + tuple: A tuple containing the stream ID and index ID.
234 + """
235 + stream_id = await get_stream_id_by_stream_name(stream_name=f"{customer_details.customer_name} - BITDEFENDER LOGS AND EVENTS")
236 + index_id = (await create_index_set(request=customer_details)).data.id
237 + content_pack_stream_id = await get_content_pack_id_by_content_pack_name(
238 + content_pack_name=f"{customer_details.customer_name}_BITDEFENDER_STREAM",
239 + )
240 + if customer_details.protocal_type == "Tcp":
241 + content_pack_input_id = await get_content_pack_id_by_content_pack_name(
242 + content_pack_name=f"{customer_details.customer_name}_BITDEFENDER_INPUT_TCP",
243 + )
244 + elif customer_details.protocal_type == "UDP":
245 + content_pack_input_id = await get_content_pack_id_by_content_pack_name(
246 + content_pack_name=f"{customer_details.customer_name}_BITDEFENDER_INPUT_SYSLOG_UDP",
247 + )
248 + return stream_id, index_id, content_pack_stream_id, content_pack_input_id
249 +
250 +
251 +#### ! GRAFANA ! ####
252 +async def create_grafana_datasource(
253 + customer_code: str,
254 + session: AsyncSession,
255 +) -> GrafanaDataSourceCreationResponse:
256 + """
257 + Creates a Grafana datasource for the specified customer.
258 +
259 + Args:
260 + customer_code (str): The customer code.
261 + session (AsyncSession): The async session.
262 +
263 + Returns:
264 + GrafanaDataSourceCreationResponse: The response containing the created datasource details.
265 + """
266 + logger.info("Creating Grafana datasource")
267 + grafana_client = await create_grafana_client("Grafana")
268 + # Switch to the newly created organization
269 + grafana_client.user.switch_actual_user_organisation(
270 + (await get_customer_meta(customer_code, session)).customer_meta.customer_meta_grafana_org_id,
271 + )
272 + datasource_payload = GrafanaDatasource(
273 + name="BITDEFENDER",
274 + type="grafana-opensearch-datasource",
275 + typeName="OpenSearch",
276 + access="proxy",
277 + url=await get_connector_attribute(
278 + connector_id=1,
279 + column_name="connector_url",
280 + session=session,
281 + ),
282 + database=f"bitdefender-{customer_code}*",
283 + basicAuth=True,
284 + basicAuthUser=await get_connector_attribute(
285 + connector_id=1,
286 + column_name="connector_username",
287 + session=session,
288 + ),
289 + secureJsonData={
290 + "basicAuthPassword": await get_connector_attribute(
291 + connector_id=1,
292 + column_name="connector_password",
293 + session=session,
294 + ),
295 + },
296 + isDefault=False,
297 + jsonData={
298 + "database": f"bitdefender-{customer_code}*",
299 + "flavor": "opensearch",
300 + "includeFrozen": False,
301 + "logLevelField": "severity",
302 + "logMessageField": "summary",
303 + "maxConcurrentShardRequests": 5,
304 + "pplEnabled": True,
305 + "timeField": "timestamp",
306 + "tlsSkipVerify": True,
307 + "version": await get_opensearch_version(),
308 + },
309 + readOnly=True,
310 + )
311 + results = grafana_client.datasource.create_datasource(
312 + datasource=datasource_payload.dict(),
313 + )
314 + return GrafanaDataSourceCreationResponse(**results)
315 +
316 +
317 +async def create_customer_network_connector_meta(
318 + customer_details,
319 + stream_id,
320 + index_id,
321 + content_pack_stream_id,
322 + content_pack_input_id,
323 + session,
324 +):
325 + """
326 + Create a CustomerNetworkConnectorsMeta object with the provided details.
327 +
328 + Args:
329 + customer_details (CustomerDetails): Details of the customer.
330 + stream_id (int): ID of the Graylog stream.
331 + index_id (int): ID of the Graylog index.
332 + session (Session): Database session.
333 +
334 + Returns:
335 + CustomerNetworkConnectorsMeta: The created CustomerNetworkConnectorsMeta object.
336 + """
337 + return CustomerNetworkConnectorsMeta(
338 + customer_code=customer_details.customer_code,
339 + network_connector_name="BITDEFENDER",
340 + graylog_stream_id=stream_id,
341 + graylog_input_id=(await get_input_id_by_input_name(input_name=f"{customer_details.customer_name} - BITDEFENDER LOGS AND EVENTS")),
342 + graylog_pipeline_id="NONE",
343 + graylog_content_pack_input_id=content_pack_input_id,
344 + graylog_content_pack_stream_id=content_pack_stream_id,
345 + grafana_org_id=(
346 + await get_customer_meta_attribute(
347 + session=session,
348 + customer_code=customer_details.customer_code,
349 + column_name="customer_meta_grafana_org_id",
350 + )
351 + ),
352 + graylog_index_id=index_id,
353 + grafana_dashboard_folder_id=None,
354 + grafana_datasource_uid=None,
355 + )
356 +
357 +
358 +async def validate_grafana_organization_id(customer_code, session):
359 + """
360 + Validate the Grafana organization ID for the customer.
361 +
362 + Args:
363 + customer_code (str): The customer code.
364 + session (Session): Database session.
365 +
366 + Returns:
367 + int: The Grafana organization ID.
368 + """
369 + return await get_customer_meta_attribute(session=session, customer_code=customer_code, column_name="customer_meta_grafana_org_id")
370 +
371 +
372 +async def provision_bitdefender(
373 + customer_details: BitdefenderCustomerDetails,
374 + keys: ProvisionBitdefenderAuthKeys,
375 + session: AsyncSession,
376 +) -> ProvisionBitdefenderResponse:
377 + """
378 + Provisions a Bitdefender customer by performing the following steps:
379 + 1. Provisions the content pack for the customer.
380 + 2. Retrieves the stream and index IDs for the customer.
381 + 3. Creates customer network connector metadata.
382 + 4. Assigns the stream to the index.
383 + 5. Retrieves the pipeline ID for the "BITDEFENDER" subscription.
384 + 6. Connects the stream to the pipeline.
385 + 7. Inserts the customer network connector metadata into the database.
386 + 8. Creates a directory for the customer to store the docker compose and falconhose cfg.
387 +
388 + Args:
389 + customer_details (BitdefenderCustomerDetails): The details of the Bitdefender customer.
390 + keys (ProvisionBitdefenderKeys): The keys required for provisioning.
391 + session (AsyncSession): The database session.
392 +
393 + Returns:
394 + None
395 + """
396 + # If customer name contains a space, replace it with a _
397 + if " " in customer_details.customer_name:
398 + customer_details.customer_name = customer_details.customer_name.replace(" ", "_")
399 + if await validate_grafana_organization_id(customer_details.customer_code, session) is None:
400 + raise HTTPException(status_code=404, detail="Grafana organization ID not found. Please provision Grafana for the customer first.")
401 + await provision_content_pack(customer_details)
402 + stream_id, index_id, content_pack_stream_id, content_pack_input_id = await get_stream_and_index_ids(customer_details)
403 + customer_network_connector_meta = await create_customer_network_connector_meta(
404 + customer_details,
405 + stream_id,
406 + index_id,
407 + content_pack_stream_id,
408 + content_pack_input_id,
409 + session,
410 + )
411 + await assign_stream_to_index(stream_id=stream_id, index_id=index_id)
412 + # ! Commenting out the pipeline ID retrieval for now since I don't have the pipeline template ! #
413 + # pipeline_id = await get_pipeline_id(subscription="BITDEFENDER")
414 + # await connect_stream_to_pipeline(stream_and_pipeline=StreamConnectionToPipelineRequest(stream_id=stream_id, pipeline_ids=pipeline_id))
415 + # Grafana Deployment
416 + customer_network_connector_meta.grafana_datasource_uid = (
417 + await create_grafana_datasource(
418 + customer_code=customer_details.customer_code,
419 + session=session,
420 + )
421 + ).datasource.uid
422 + grafana_folder = await create_grafana_folder(
423 + organization_id=(
424 + await get_customer_meta(
425 + customer_details.customer_code,
426 + session,
427 + )
428 + ).customer_meta.customer_meta_grafana_org_id,
429 + folder_title="BITDEFENDER",
430 + )
431 + await provision_dashboards(
432 + DashboardProvisionRequest(
433 + dashboards=[dashboard.name for dashboard in BitdefenderDashboard],
434 + organizationId=(
435 + await get_customer_meta(
436 + customer_details.customer_code,
437 + session,
438 + )
439 + ).customer_meta.customer_meta_grafana_org_id,
440 + folderId=grafana_folder.id,
441 + datasourceUid=customer_network_connector_meta.grafana_datasource_uid,
442 + ),
443 + )
444 + customer_network_connector_meta.grafana_dashboard_folder_id = grafana_folder.uid
445 + await insert_into_customer_network_connectors_meta_table(
446 + customer_network_connectors_meta=customer_network_connector_meta,
447 + session=session,
448 + )
449 + await create_customer_directory_if_needed(customer_name=customer_details.customer_name)
450 + file = await load_and_replace_docker_compose(customer_name=customer_details.customer_name, port=keys.WEBSERVER_PORT)
451 + await save_uploaded_file(
452 + file=file,
453 + filename=f"{customer_details.customer_name}_bitdefender_docker-compose.yml",
454 + customer_name=customer_details.customer_name,
455 + )
456 + await load_and_replace_config_json(customer_details=customer_details, keys=keys, session=session)
457 + await update_customer_integration_table(
458 + customer_code=customer_details.customer_code,
459 + session=session,
460 + )
461 +
462 + await send_configuration_to_bitdefender(keys)
463 +
464 + return ProvisionBitdefenderResponse(
465 + message="Bitdefender customer provisioned successfully",
466 + success=True,
467 + )
468 +
469 +
470 +async def insert_into_customer_network_connectors_meta_table(
471 + customer_network_connectors_meta: CustomerNetworkConnectorsMeta,
472 + session: AsyncSession,
473 +) -> None:
474 + """
475 + Insert the customer network connectors meta into the database.
476 +
477 + Args:
478 + customer_network_connectors_meta (CustomerNetworkConnectorsMeta): The customer network connectors meta to insert.
479 + session (AsyncSession): The async session object for database operations.
480 +
481 + Returns:
482 + None
483 + """
484 + logger.info("Inserting customer network connectors meta into the database")
485 + session.add(customer_network_connectors_meta)
486 + await session.commit()
487 + logger.info("Customer network connectors meta inserted successfully")
488 + return None
489 +
490 +
491 +# ! Add the docker-compose.yml file to the `data` folder
492 +project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
493 +UPLOAD_FOLDER = os.path.join(project_root, "data")
494 +
495 +
496 +async def create_customer_directory_if_needed(customer_name: str):
497 + """
498 + Create a directory for the customer in the UPLOAD_FOLDER if it doesn't exist.
499 +
500 + Args:
501 + customer_name (str): The name of the customer.
502 + """
503 + # Create the path to the customer's directory
504 + # If customer name contains a space, replace it with a _
505 + if " " in customer_name:
506 + customer_name = customer_name.replace(" ", "_")
507 + customer_directory = os.path.join(UPLOAD_FOLDER, customer_name)
508 + # Check if the directory exists
509 + if not os.path.exists(customer_directory):
510 + # If it doesn't exist, create it
511 + os.makedirs(customer_directory)
512 +
513 +
514 +async def load_and_replace_docker_compose(customer_name: str, port: str):
515 + """
516 + Load the docker-compose.yml file and replace the placeholder with the customer name.
517 +
518 + Args:
519 + customer_name (str): The name of the customer.
520 +
521 + Returns:
522 + str: The content of the docker-compose.yml file with the placeholder replaced.
523 + """
524 + # Get the current directory:
525 + current_directory = os.path.dirname(os.path.abspath(__file__))
526 + # Go up one level
527 + parent_directory = os.path.dirname(current_directory)
528 + # If customer name contains a space, replace it with a _
529 + if " " in customer_name:
530 + customer_name = customer_name.replace(" ", "_")
531 + # Open the docker-compose.yml file and read the content
532 + with open(os.path.join(parent_directory, "templates", "docker-compose.yml"), "r") as file:
533 + data = file.read()
534 + data = data.replace("CUSTOMER_NAME", customer_name)
535 + data = data.replace("PORT", port)
536 + return data
537 +
538 +
539 +async def save_uploaded_file(file, filename, customer_name):
540 + """
541 + Save the uploaded file to the server.
542 +
543 + Args:
544 + file: The file to save.
545 + filename: The name of the file.
546 +
547 + Returns:
548 + str: The path to the saved file.
549 + """
550 + # If customer name contains a space, replace it with a _
551 + if " " in customer_name:
552 + customer_name = customer_name.replace(" ", "_")
553 + customer_upload_folder = os.path.join(UPLOAD_FOLDER, customer_name)
554 + async with aiofiles.open(os.path.join(customer_upload_folder, filename), "wb") as f:
555 + await f.write(file.encode())
556 + return os.path.join(customer_upload_folder, filename)
557 +
558 +
559 +async def base64_auth_header_generator(username: str, password: str):
560 + """
561 + Generate the basic authentication header for the given username and password.
562 +
563 + Args:
564 + username (str): The username.
565 + password (str): The password.
566 +
567 + Returns:
568 + str: The basic authentication header.
569 + """
570 + return f"Basic {base64.b64encode(f'{username}:{password}'.encode()).decode()}"
571 +
572 +
573 +async def load_and_replace_config_json(
574 + customer_details: BitdefenderCustomerDetails,
575 + keys: ProvisionBitdefenderAuthKeys,
576 + session: AsyncSession,
577 +):
578 + """
579 + Load the config.json file and replace the placeholders with the customer details.
580 +
581 + Args:
582 + customer_details (BitdefenderCustomerDetails): The details of the customer.
583 + keys (ProvisionBitdefenderAuthKeys): The authentication keys for Bitdefender.
584 +
585 + Returns:
586 + str: The path to the modified config.json file.
587 + """
588 + connector_url = str(await get_connector_attribute(connector_id=3, column_name="connector_url", session=session))
589 + connector_url = connector_url.replace("https://", "").replace("http://", "").replace(":9000", "")
590 + encoded_string = await base64_auth_header_generator(keys.BASIC_AUTH_USERNAME, keys.BASIC_AUTH_PASSWORD)
591 +
592 + # Define the JSON structure
593 + data = {
594 + "port": int(keys.WEBSERVER_PORT),
595 + "syslog_port": int(keys.GRAYLOG_PORT),
596 + "transport": customer_details.protocal_type,
597 + "target": connector_url,
598 + "authentication_string": encoded_string,
599 + "secure": {
600 + "enabled": True,
601 + "key": "api/config/server.key",
602 + "cert": "api/config/server.crt",
603 + },
604 + }
605 +
606 + # If customer name contains a space, replace it with a _
607 + if " " in customer_details.customer_name:
608 + customer_details.customer_name = customer_details.customer_name.replace(" ", "_")
609 +
610 + customer_upload_folder = os.path.join(UPLOAD_FOLDER, customer_details.customer_name)
611 + os.makedirs(customer_upload_folder, exist_ok=True)
612 +
613 + # Save the modified content back to the config.json file
614 + config_path = os.path.join(customer_upload_folder, "config.json")
615 + async with aiofiles.open(config_path, "w") as f:
616 + await f.write(json.dumps(data, indent=4))
617 +
618 + return config_path
619 +
620 +
621 +async def update_customer_integration_table(
622 + customer_code: str,
623 + session: AsyncSession,
624 +) -> None:
625 + """
626 + Updates the `customer_integrations` table to set the `deployed` column to True where the `customer_code`
627 + matches the given customer code and the `integration_service_name` is "Bitdefender".
628 +
629 + Args:
630 + customer_code (str): The customer code.
631 + session (AsyncSession): The async session object for making HTTP requests.
632 + """
633 + logger.info(f"Updating customer integrations table for customer {customer_code}")
634 + await session.execute(
635 + update(CustomerIntegrations)
636 + .where(
637 + and_(
638 + CustomerIntegrations.customer_code == customer_code,
639 + CustomerIntegrations.integration_service_name == "BitDefender",
640 + ),
641 + )
642 + .values(deployed=True),
643 + )
644 + await session.commit()
645 +
646 + return None
backend/app/integrations/bitdefender/templates/docker-compose.yml new
+10
@@ -0,0 +1,10 @@
1 +version: "3.8"
2 +
3 +services:
4 + bitdefender-connector-CUSTOMER_NAME:
5 + image: ghcr.io/socfortress/bitdefender-connector
6 + volumes:
7 + - /opt/CoPilot/data/data/CUSTOMER_NAME/config.json:/opt/bitdefender/gz-evpsc/api/config/config.json
8 + ports:
9 + - "PORT:PORT"
10 + restart: unless-stopped
backend/app/integrations/markdown/bitdefender.md new
+103
@@ -0,0 +1,103 @@
1 +# [BitDefender Integration](https://www.bitdefender.com/business/support/en/77209-144080-build-an-event-push-service-api-connector-for-cef-standard.html)
2 +
3 +## Prerequisites
4 +
5 +Before using the GravityZone Event Push Service API Connector, you’ll want to first define the API client and set its scope. Refer to this guide (https://www.bitdefender.com/business/support/en/77209-125277-public-api.html?srsltid=AfmBOooClMFH-dCfndiVD6eDNTBM5Q13qoFafF04EO3EsdJBPIbm--kx) to getting access to the BitDefender API for setting up a new API client key. For the new API client, make sure the scope includes read access for Event streams.
6 +
7 +![BitDefender API Settings](/images/bitdefender/bitdefender_api_key.png)
8 +
9 +### Firewall Configuration
10 +
11 +The connector uses the POST method to receive authenticated and secured messages from the GravityZone Event Push Service. It parses the message and then forwards it to a local or a remote Syslog server. You can use the Syslog server to feed these messages to the SIEM. This means that the connector must be able to receive messages from the GravityZone Event Push Service and send them to the Graylog server. Make sure that the firewall allows the connector to receive messages from the GravityZone Event Push Service and send them to the Graylog server.
12 +
13 +What we will be deploying is an HTTP endpoint that will receive the BitDefender logs and forward them to a syslog server. The HTTP endpoint is running on the server running CoPilot and will be listening on the port that you will define. This means that we must configure public DNS records to point to your edge firewall and open the port that you will define in the firewall.
14 +
15 +Traffic flow will be as follows:
16 +BitDefender Cloud Platform -> Your Edge Firewall -> CoPilot Server -> Graylog Server
17 +
18 +## Configuration
19 +
20 +The configuration for our API creds and syslog forwarder settings are stored within `/opt/bitdefender/gz-evpsc/api/config/config.json`. Adjust to make your changes. **NOTE that the `port` , `syslog_port` , `target` , and `authentication_string` will need to be updated.** Below is an example, CoPilot will take care of this for you.
21 +
22 +```json
23 +{
24 + "port": 3200,
25 + "syslog_port": 10514,
26 + "transport": "Tcp",
27 + "target": "YOUR_GRAYLOG_SERVER",
28 + "authentication_string": "Basic cmVsaWFibGVwYnhfYml0ZGVmZW5kZXI6cmVsaWFibGVwYnhfYml0ZGVmZW5kZXI=",
29 + "secure": {
30 + "enabled": true,
31 + "key": "api/config/server.key",
32 + "cert": "api/config/server.crt"
33 + }
34 +}
35 +```
36 +
37 +## Provisioning
38 +
39 +Once you have saved the BitDefender configuration for the customer, you are ready to deploy the integration. Navigate to the `Customers` tab and select the appropriate customer. The provisiong creates the necessary:
40 +
41 +- Graylog CEF Input
42 +- Graylog Stream
43 +- Graylog Index
44 +- Grafana Datasource
45 +- Grafana Dashboards
46 +- BitDefender Docker-Compose File
47 +
48 +## Deployment of BitDefender Container
49 +
50 +The BitDefender integration runs via a docker container. During provisioning, the following directory is created `/opt/CoPilot/data/data/CUSTOMER_NAME`. Within this directory will reside the `CUSTOMER_NAME_bitdefender_docker-compose.yml` and the `config.json` files. These can be modified if desired but should already contain the details needed to collect logs for their BitDefender environment.
51 +
52 +Start the container with the below command:
53 +
54 +```bash
55 +docker compose -f /opt/CoPilot/data/data/CUSTOMER_NAME/CUSTOMER_NAME_bitdefender_docker-compose.yml up -d
56 +```
57 +
58 +You should now see the container running.
59 +
60 +## Test the Connector
61 +
62 +[Helpful Doc](https://support.netenrich.com/hc/en-us/articles/10833633251869-Bitdefender-Gravity-Zone-Cloud-integration#:~:text=155.173,Configure%20Chronicle%20Forwarder)
63 +
64 +Use the following cURL command to send the test payload to the collector service you have just configured:
65 +
66 +Replace `YOUR_AUTH_HEADER` with the base64 (https://www.blitter.se/utils/basic-authentication-header-generator/) encoded string of `username:password` and `REPLACE_WITH_YOUR_WEBSERVER` with the public DNS name you configured.
67 +
68 +### NOTE: This only tests that your endpoint is reachable and that the logs are being sent to the endpoint. You will need to verify that the logs are being sent to the Graylog server.
69 +
70 +```bash
71 +curl -k -H 'Authorization: Basic YOUR_AUTH_HEADER' -H "Content-Type: application/json" -d
72 +'{"cef": "0","events":
73 +["CEF:0|Bitdefender|GravityZone|6.4.08|70000|Registration|3|BitdefenderGZModule=registrationd
74 +vchost=TEST_ENDPOINTasdadBitdefenderGZComputerFQDN=test.example.com
75 +dvc=192.168.1.2","CEF:0|Bitdefender|GravityZone|6.4.0-8|35|
76 +Product ModulesStatus|5|BitdefenderGZModule=modules
77 +dvchost=TEST_ENDPOINTasdadBitdefenderGZComputerFQDN=test.example.com
78 +dvc=192.168.1.2","CEF:0|Bitdefender|GravityZone|6.4.0-8|35|
79 +Product ModulesStatus|5|BitdefenderGZModule=modules
80 +dvchost=TEST_ENDPOINTasdadBitdefenderGZComputerFQDN=test.example.com dvc=192.168.1.2"]}'
81 +https://REPLACE_WITH_YOUR_WEBSERVER:3200/api
82 +```
83 +
84 +Now that the HTTPS collector service is running and listening for messages, we can test the service by sending a test message to the BitDefender service. Use the following cURL command to send the test payload to the collector service you have just configured:
85 +
86 +Replace `YOUR_BITDEFENDER_API_KEY` with the BitDefender API key with the base64 encoded string of `API_KEY` followed by a colon `:`. For example, if the API key is `test`, the value I would base64 encode would be `test:`. Replace `REPLACE_WITH_YOUR_WEBSERVER` with the public DNS name you configured.
87 +
88 +```bash
89 +$ curl --tlsv1.2 -sS -k -X POST \
90 +https://cloud.gravityzone.bitdefender.com/api/v1.0/jsonrpc/push \
91 +-H 'authorization: Basic YOUR_BITDEFENDER_API_KEY' \
92 +-H 'cache-control: no-cache' \
93 +-H 'content-type: application/json' \
94 +-d '{"id":"1","jsonrpc":"2.0","method":"setPushEventSettings",
95 +"params":{"serviceSettings":{"requireValidSslCertificate":false,"authorization":"Basic
96 +dGVzdDp0ZXN0","url":"https://REPLACE_WITH_YOUR_WEBSERVER:3200/api"},"serviceType":"jsonRPC","status":1,
97 +"subscribeToEventTypes":{"adcloudgz":true,"antiexploit":true,"aph":true,"av":true,"avc":true,"dp":true,
98 +"endpoint-moved-in":true,"endpoint-moved-out":true,"exchange-malware":true,
99 +"exchange-user-credentials":true,"fw":true,"hd":true,"hwid-change":true,"install":true,"modules":true,
100 +"network-monitor":true,"network-sandboxing":true,"new-incident":true,"ransomware-mitigation":true,
101 +"registration":true,"supa-update-status":true,"sva":true,"sva-load":true,"task-status":true,
102 +"troubleshooting-activity":true,"uc":true,"uninstall":true}}}'
103 +```
backend/app/routers/bitdefender.py new
+13
@@ -0,0 +1,13 @@
1 +from fastapi import APIRouter
2 +
3 +from app.integrations.bitdefender.routes.provision import integration_bitdefender_router
4 +
5 +# Instantiate the APIRouter
6 +router = APIRouter()
7 +
8 +# Include the BitDefender related routes
9 +router.include_router(
10 + integration_bitdefender_router,
11 + prefix="/bitdefender",
12 + tags=["BitDefender"],
13 +)
backend/app/stack_provisioning/graylog/schema/provision.py
+2
@@ -27,6 +27,8 @@ class AvailableContentPacks(str, Enum):
27 SOCFORTRESS_CROWDSTRIKE_INPUT_TCP = "The Crowdstrike Input TCP content pack"
28 SOCFORTRESS_CROWDSTRIKE_STREAM = "The Crowdstrike Stream content pack"
29 SOCFORTRESS_CROWDSTRIKE_PROCESSING_PIPELINE = "The Crowdstrike Processing Pipeline content pack"
30 + SOCFORTRESS_BITDEFENDER_INPUT_TCP = "The Bitdefender Input TCP content pack"
31 + SOCFORTRESS_BITDEFENDER_STREAM = "The Bitdefender Stream content pack"
32
33
34 class ContentPackKeywords(BaseModel):
backend/app/stack_provisioning/graylog/templates/SOCFORTRESS_BITDEFENDER_INPUT_TCP.json new
+118
@@ -0,0 +1,118 @@
1 +{
2 + "v": 1,
3 + "id": "REPLACE_UUID_GLOBAL",
4 + "rev": 1,
5 + "name": "customer_name_BITDEFENDER_INPUT_TCP",
6 + "summary": "customer_name_BITDEFENDER_INPUT_TCP",
7 + "description": "",
8 + "vendor": "SOCFortress",
9 + "url": "",
10 + "parameters": [],
11 + "entities": [
12 + {
13 + "v": "1",
14 + "type": {
15 + "name": "input",
16 + "version": "1"
17 + },
18 + "id": "REPLACE_UUID_SPECIFIC",
19 + "data": {
20 + "title": {
21 + "@type": "string",
22 + "@value": "customer_name - BITDEFENDER LOGS AND EVENTS"
23 + },
24 + "configuration": {
25 + "tls_key_file": {
26 + "@type": "string",
27 + "@value": ""
28 + },
29 + "port": {
30 + "@type": "integer",
31 + "@value": "SYSLOG_PORT"
32 + },
33 + "tls_enable": {
34 + "@type": "boolean",
35 + "@value": false
36 + },
37 + "use_null_delimiter": {
38 + "@type": "boolean",
39 + "@value": false
40 + },
41 + "recv_buffer_size": {
42 + "@type": "integer",
43 + "@value": 1048576
44 + },
45 + "tcp_keepalive": {
46 + "@type": "boolean",
47 + "@value": false
48 + },
49 + "use_full_names": {
50 + "@type": "boolean",
51 + "@value": false
52 + },
53 + "tls_client_auth_cert_file": {
54 + "@type": "string",
55 + "@value": ""
56 + },
57 + "bind_address": {
58 + "@type": "string",
59 + "@value": "0.0.0.0"
60 + },
61 + "tls_cert_file": {
62 + "@type": "string",
63 + "@value": ""
64 + },
65 + "max_message_size": {
66 + "@type": "integer",
67 + "@value": 2097152
68 + },
69 + "tls_client_auth": {
70 + "@type": "string",
71 + "@value": "disabled"
72 + },
73 + "timezone": {
74 + "@type": "string",
75 + "@value": "Etc/UTC"
76 + },
77 + "locale": {
78 + "@type": "string",
79 + "@value": ""
80 + },
81 + "number_worker_threads": {
82 + "@type": "integer",
83 + "@value": 8
84 + },
85 + "tls_key_password": {
86 + "@type": "string",
87 + "@value": ""
88 + }
89 + },
90 + "static_fields": {
91 + "syslog_type": {
92 + "@type": "string",
93 + "@value": "bitdefender"
94 + },
95 + "syslog_customer": {
96 + "@type": "string",
97 + "@value": "customer_code"
98 + }
99 + },
100 + "type": {
101 + "@type": "string",
102 + "@value": "org.graylog.plugins.cef.input.CEFTCPInput"
103 + },
104 + "global": {
105 + "@type": "boolean",
106 + "@value": false
107 + },
108 + "extractors": []
109 + },
110 + "constraints": [
111 + {
112 + "type": "server-version",
113 + "version": ">=5.0.13+083613e"
114 + }
115 + ]
116 + }
117 + ]
118 +}
backend/app/stack_provisioning/graylog/templates/SOCFORTRESS_BITDEFENDER_STREAM.json new
+102
@@ -0,0 +1,102 @@
1 +{
2 + "v": 1,
3 + "id": "REPLACE_UUID_GLOBAL",
4 + "rev": 1,
5 + "name": "customer_name_BITDEFENDER_STREAM",
6 + "summary": "customer_name_BITDEFENDER_STREAM",
7 + "description": "",
8 + "vendor": "SOCFortress",
9 + "url": "",
10 + "parameters": [],
11 + "entities": [
12 + {
13 + "v": "1",
14 + "type": {
15 + "name": "stream",
16 + "version": "1"
17 + },
18 + "id": "REPLACE_UUID_SPECIFIC",
19 + "data": {
20 + "alarm_callbacks": [],
21 + "outputs": [],
22 + "remove_matches": {
23 + "@type": "boolean",
24 + "@value": true
25 + },
26 + "title": {
27 + "@type": "string",
28 + "@value": "customer_name - BITDEFENDER LOGS AND EVENTS"
29 + },
30 + "stream_rules": [
31 + {
32 + "type": {
33 + "@type": "string",
34 + "@value": "EXACT"
35 + },
36 + "field": {
37 + "@type": "string",
38 + "@value": "syslog_type"
39 + },
40 + "value": {
41 + "@type": "string",
42 + "@value": "bitdefender"
43 + },
44 + "inverted": {
45 + "@type": "boolean",
46 + "@value": false
47 + },
48 + "description": {
49 + "@type": "string",
50 + "@value": ""
51 + }
52 + },
53 + {
54 + "type": {
55 + "@type": "string",
56 + "@value": "EXACT"
57 + },
58 + "field": {
59 + "@type": "string",
60 + "@value": "syslog_customer"
61 + },
62 + "value": {
63 + "@type": "string",
64 + "@value": "customer_code"
65 + },
66 + "inverted": {
67 + "@type": "boolean",
68 + "@value": false
69 + },
70 + "description": {
71 + "@type": "string",
72 + "@value": ""
73 + }
74 + }
75 + ],
76 + "alert_conditions": [],
77 + "matching_type": {
78 + "@type": "string",
79 + "@value": "AND"
80 + },
81 + "disabled": {
82 + "@type": "boolean",
83 + "@value": false
84 + },
85 + "description": {
86 + "@type": "string",
87 + "@value": "customer_name - BITDEFENDER LOGS AND EVENTS"
88 + },
89 + "default_stream": {
90 + "@type": "boolean",
91 + "@value": false
92 + }
93 + },
94 + "constraints": [
95 + {
96 + "type": "server-version",
97 + "version": ">=5.0.13+083613e"
98 + }
99 + ]
100 + }
101 + ]
102 +}
backend/copilot.py
+2
@@ -34,6 +34,7 @@ from app.routers import active_response
34 from app.routers import agents
35 from app.routers import alert_creation_settings
36 from app.routers import auth
37 +from app.routers import bitdefender
38 from app.routers import carbonblack
39 from app.routers import connectors
40 from app.routers import cortex
@@ -148,6 +149,7 @@ api_router.include_router(modules.router)
149 api_router.include_router(carbonblack.router)
150 api_router.include_router(network_connectors.router)
151 api_router.include_router(crowdstrike.router)
152 +api_router.include_router(bitdefender.router)
153 api_router.include_router(scoutsuite.router)
154 api_router.include_router(nuclei.router)
155 api_router.include_router(duo.router)
frontend/package-lock.json
+588 -174
@@ -10,11 +10,11 @@
10 "dependencies": {
11 "@ajoelp/json-to-formdata": "^1.5.0",
12 "@f3ve/vue-markdown-it": "^0.2.2",
13 - "@fontsource/jetbrains-mono": "^5.0.21",
14 - "@fontsource/lexend": "^5.0.21",
13 + "@fontsource/jetbrains-mono": "^5.0.22",
14 + "@fontsource/lexend": "^5.0.22",
15 "@fontsource/public-sans": "^5.0.18",
16 "@popperjs/core": "^2.11.8",
17 - "@shikijs/markdown-it": "^1.16.2",
17 + "@shikijs/markdown-it": "^1.17.0",
18 "@vueuse/components": "^11.0.3",
19 "@vueuse/core": "^11.0.3",
20 "apexcharts": "^3.53.0",
@@ -34,15 +34,15 @@
34 "naive-ui": "^2.39.0",
35 "password-validator": "^5.3.0",
36 "pinia": "^2.2.2",
37 - "pinia-plugin-persistedstate": "^4.0.0",
37 + "pinia-plugin-persistedstate": "^4.0.1",
38 "secure-ls": "^2.0.0",
39 - "shiki": "^1.16.2",
39 + "shiki": "^1.17.0",
40 "validator": "^13.12.0",
41 - "vue": "^3.5.3",
41 + "vue": "^3.5.4",
42 "vue-advanced-cropper": "^2.8.9",
43 "vue-highlight-words": "^3.0.1",
44 - "vue-i18n": "^9.14.0",
45 - "vue-router": "^4.4.3",
44 + "vue-i18n": "^10.0.0",
45 + "vue-router": "^4.4.4",
46 "vue-sjv": "^0.0.6",
47 "vue3-apexcharts": "^1.6.0",
48 "vue3-marquee": "^4.2.2",
@@ -68,7 +68,7 @@
68 "@vue/tsconfig": "^0.5.1",
69 "autoprefixer": "^10.4.20",
70 "cypress": "^13.14.2",
71 - "eslint": "^9.9.1",
71 + "eslint": "^9.10.0",
72 "eslint-plugin-cypress": "^3.5.0",
73 "eslint-plugin-vue": "^9.28.0",
74 "flourite": "^1.3.0",
@@ -81,16 +81,16 @@
81 "postcss": "^8.4.45",
82 "prettier": "^3.3.3",
83 "sass": "^1.78.0",
84 - "start-server-and-test": "^2.0.5",
84 + "start-server-and-test": "^2.0.7",
85 "tailwind-config-viewer": "^2.0.4",
86 "tailwindcss": "^3.4.10",
87 "taze": "^0.16.7",
88 - "type-fest": "^4.26.0",
88 + "type-fest": "^4.26.1",
89 "unplugin-vue-components": "^0.27.4",
90 - "vite": "^5.4.3",
90 + "vite": "^5.4.4",
91 "vite-bundle-analyzer": "^0.10.6",
92 "vite-bundle-visualizer": "^1.2.1",
93 - "vite-plugin-vue-devtools": "^7.4.4",
93 + "vite-plugin-vue-devtools": "^7.4.5",
94 "vite-svg-loader": "^5.1.0",
95 "vitest": "^2.0.5",
96 "vue-tsc": "^2.1.6"
@@ -1247,6 +1247,17 @@
1247 "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1248 }
1249 },
1250 + "node_modules/@eslint/plugin-kit": {
1251 + "version": "0.1.0",
1252 + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.1.0.tgz",
1253 + "integrity": "sha512-autAXT203ixhqei9xt+qkYOvY8l6LAFIdT2UXc/RPNeUVfqRF1BV94GTJyVPFKT8nFM6MyVJhjLj9E8JWvf5zQ==",
1254 + "dependencies": {
1255 + "levn": "^0.4.1"
1256 + },
1257 + "engines": {
1258 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1259 + }
1260 + },
1261 "node_modules/@f3ve/eslint-config": {
1262 "version": "1.0.6",
1263 "resolved": "https://registry.npmjs.org/@f3ve/eslint-config/-/eslint-config-1.0.6.tgz",
@@ -1402,14 +1413,14 @@
1413 }
1414 },
1415 "node_modules/@fontsource/jetbrains-mono": {
1405 - "version": "5.0.21",
1406 - "resolved": "https://registry.npmjs.org/@fontsource/jetbrains-mono/-/jetbrains-mono-5.0.21.tgz",
1407 - "integrity": "sha512-BjI7a4U4HC4R3rEq96dN4CKtH8qxHFA+zU+6Ftusn/9k+VbF+VZ1l5bmHCUtg2ekjXbGF955/1FODlCKXleaGQ=="
1416 + "version": "5.0.22",
1417 + "resolved": "https://registry.npmjs.org/@fontsource/jetbrains-mono/-/jetbrains-mono-5.0.22.tgz",
1418 + "integrity": "sha512-eFjS3ilq9n9FeRCdSSDhj3BTtr74FYeJswSJr9VDIxb02A07sQ59VugLuq1EKs6eLha+IOT5+4qVpllGaQNNHg=="
1419 },
1420 "node_modules/@fontsource/lexend": {
1410 - "version": "5.0.21",
1411 - "resolved": "https://registry.npmjs.org/@fontsource/lexend/-/lexend-5.0.21.tgz",
1412 - "integrity": "sha512-y/rRR0B77mSEOqBKZKixT0b9h9Rnqpnyk/ZIqZv9yvxyAJWLEjUwc1GhkagdpsUOB0Sn6zVk1V4PNespcjSxZA=="
1421 + "version": "5.0.22",
1422 + "resolved": "https://registry.npmjs.org/@fontsource/lexend/-/lexend-5.0.22.tgz",
1423 + "integrity": "sha512-Eb1T9KamX8rK4dCl3t/rL9a0uFp2/L477HO0I11RIZWdrDR3iQ4vx6L5hd866VrzH6H0TGPLfuW+jstbZeFeWA=="
1424 },
1425 "node_modules/@fontsource/public-sans": {
1426 "version": "5.0.18",
@@ -1477,12 +1488,12 @@
1488 }
1489 },
1490 "node_modules/@intlify/core-base": {
1480 - "version": "9.14.0",
1481 - "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.14.0.tgz",
1482 - "integrity": "sha512-zJn0imh9HIsZZUtt9v8T16PeVstPv6bP2YzlrYJwoF8F30gs4brZBwW2KK6EI5WYKFi3NeqX6+UU4gniz5TkGg==",
1491 + "version": "10.0.0",
1492 + "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-10.0.0.tgz",
1493 + "integrity": "sha512-o4d4Nve7YzU1YMR5VMqgPr8jDGTgT2pOOUtZa3JwCAhFnm40JYxfHdWToT7OEx6oJCBs/Q8HosJOhsimlF0C0Q==",
1494 "dependencies": {
1484 - "@intlify/message-compiler": "9.14.0",
1485 - "@intlify/shared": "9.14.0"
1495 + "@intlify/message-compiler": "10.0.0",
1496 + "@intlify/shared": "10.0.0"
1497 },
1498 "engines": {
1499 "node": ">= 16"
@@ -1492,11 +1503,11 @@
1503 }
1504 },
1505 "node_modules/@intlify/message-compiler": {
1495 - "version": "9.14.0",
1496 - "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.14.0.tgz",
1497 - "integrity": "sha512-sXNsoMI0YsipSXW8SR75drmVK56tnJHoYbPXUv2Cf9lz6FzvwsosFm6JtC1oQZI/kU+n7qx0qRrEWkeYFTgETA==",
1506 + "version": "10.0.0",
1507 + "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-10.0.0.tgz",
1508 + "integrity": "sha512-OcaWc63NC/9p1cMdgoNKBj4d61BH8sUW1Hfs6YijTd9656ZR4rNqXAlRnBrfS5ABq0vjQjpa8VnyvH9hK49yBw==",
1509 "dependencies": {
1499 - "@intlify/shared": "9.14.0",
1510 + "@intlify/shared": "10.0.0",
1511 "source-map-js": "^1.0.2"
1512 },
1513 "engines": {
@@ -1507,9 +1518,9 @@
1518 }
1519 },
1520 "node_modules/@intlify/shared": {
1510 - "version": "9.14.0",
1511 - "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.14.0.tgz",
1512 - "integrity": "sha512-r+N8KRQL7LgN1TMTs1A2svfuAU0J94Wu9wWdJVJqYsoMMLIeJxrPjazihfHpmJqfgZq0ah3Y9Q4pgWV2O90Fyg==",
1521 + "version": "10.0.0",
1522 + "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-10.0.0.tgz",
1523 + "integrity": "sha512-6ngLfI7DOTew2dcF9WMJx+NnMWghMBhIiHbGg+wRvngpzD5KZJZiJVuzMsUQE1a5YebEmtpTEfUrDp/NqVGdiw==",
1524 "engines": {
1525 "node": ">= 16"
1526 },
@@ -2087,36 +2098,68 @@
2098 ]
2099 },
2100 "node_modules/@shikijs/core": {
2090 - "version": "1.16.2",
2091 - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.16.2.tgz",
2092 - "integrity": "sha512-XSVH5OZCvE4WLMgdoBqfPMYmGHGmCC3OgZhw0S7KcSi2XKZ+5oHGe71GFnTljgdOxvxx5WrRks6QoTLKrl1eAA==",
2101 + "version": "1.17.0",
2102 + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.17.0.tgz",
2103 + "integrity": "sha512-Mkk4Mp4bNnW1kytU8I7S5PK5teNSe0iKlfqxPss4sdwnlcU8a2N62Z3te2gVmZfU9t1HF6L3wyWuM43IvEeEsg==",
2104 "dependencies": {
2094 - "@shikijs/vscode-textmate": "^9.2.0",
2095 - "@types/hast": "^3.0.4"
2105 + "@shikijs/engine-javascript": "1.17.0",
2106 + "@shikijs/engine-oniguruma": "1.17.0",
2107 + "@shikijs/types": "1.17.0",
2108 + "@shikijs/vscode-textmate": "^9.2.2",
2109 + "@types/hast": "^3.0.4",
2110 + "hast-util-to-html": "^9.0.2"
2111 + }
2112 + },
2113 + "node_modules/@shikijs/engine-javascript": {
2114 + "version": "1.17.0",
2115 + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-1.17.0.tgz",
2116 + "integrity": "sha512-EiBVlxmzJZdC2ypzn8k+vxLngbBNgHLS4RilwrFOABGRc72kUZubbD/6Chrq2RcVtD3yq1GtiiIdFMGd9BTX3Q==",
2117 + "dependencies": {
2118 + "@shikijs/types": "1.17.0",
2119 + "oniguruma-to-js": "0.3.3",
2120 + "regex": "4.3.2"
2121 + }
2122 + },
2123 + "node_modules/@shikijs/engine-oniguruma": {
2124 + "version": "1.17.0",
2125 + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.17.0.tgz",
2126 + "integrity": "sha512-nsXzJGLQ0fhKmA4Gwt1cF7vC8VuZ1HSDrTRuj48h/qDeX/TzmOlTDXQ3uPtyuhyg/2rbZRzNhN8UFU4fSnQfXg==",
2127 + "dependencies": {
2128 + "@shikijs/types": "1.17.0",
2129 + "@shikijs/vscode-textmate": "^9.2.2"
2130 }
2131 },
2132 "node_modules/@shikijs/markdown-it": {
2099 - "version": "1.16.2",
2100 - "resolved": "https://registry.npmjs.org/@shikijs/markdown-it/-/markdown-it-1.16.2.tgz",
2101 - "integrity": "sha512-GSddEuOaVMHOmfWc3MYyf59WS7VTXwTfsDYmM2iVBe8N4yhA9bCBUkGXYKfod7hclFI2kLiz7BqaGTzsDEXUEw==",
2133 + "version": "1.17.0",
2134 + "resolved": "https://registry.npmjs.org/@shikijs/markdown-it/-/markdown-it-1.17.0.tgz",
2135 + "integrity": "sha512-jf2MIE175wEnREogwjz+uUKeh/wPom6jTKEWMezfHQj/gaH8vJnFIBo5fUQqrDYb3zzMdeYPBsNaFRwVZTRquw==",
2136 "dependencies": {
2103 - "@shikijs/transformers": "1.16.2",
2137 + "@shikijs/transformers": "1.17.0",
2138 "markdown-it": "^14.1.0",
2105 - "shiki": "1.16.2"
2139 + "shiki": "1.17.0"
2140 }
2141 },
2142 "node_modules/@shikijs/transformers": {
2109 - "version": "1.16.2",
2110 - "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-1.16.2.tgz",
2111 - "integrity": "sha512-AR6ANiKwi1dJr5g/W0L+Su4PoHurkHLgtNmesbOFOPGKNQC2BeGU/Z2Ghkl+cUF5PfE+UeLkxUwzpE6H37hTSg==",
2143 + "version": "1.17.0",
2144 + "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-1.17.0.tgz",
2145 + "integrity": "sha512-C/s6z0knkWBVRG6cmklTUn+70URKVE8qlQuFg6Bi/9iqpX68NZloZhxCtVHeTd56pit15hivTlgSAY+SP7C8hA==",
2146 + "dependencies": {
2147 + "shiki": "1.17.0"
2148 + }
2149 + },
2150 + "node_modules/@shikijs/types": {
2151 + "version": "1.17.0",
2152 + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.17.0.tgz",
2153 + "integrity": "sha512-Tvu2pA69lbpXB+MmgIaROP1tio8y0uYvKb5Foh3q0TJBTAJuaoa5eDEtS/0LquyveacsiVrYF4uEZILju+7Ybg==",
2154 "dependencies": {
2113 - "shiki": "1.16.2"
2155 + "@shikijs/vscode-textmate": "^9.2.2",
2156 + "@types/hast": "^3.0.4"
2157 }
2158 },
2159 "node_modules/@shikijs/vscode-textmate": {
2117 - "version": "9.2.0",
2118 - "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-9.2.0.tgz",
2119 - "integrity": "sha512-5FinaOp6Vdh/dl4/yaOTh0ZeKch+rYS8DUb38V3GMKYVkdqzxw53lViRKUYkVILRiVQT7dcPC7VvAKOR73zVtQ=="
2160 + "version": "9.2.2",
2161 + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-9.2.2.tgz",
2162 + "integrity": "sha512-TMp15K+GGYrWlZM8+Lnj9EaHEFmOen0WJBrfa17hF7taDOYthuPPV0GWzfd/9iMij0akS/8Yw2ikquH7uVi/fg=="
2163 },
2164 "node_modules/@sideway/address": {
2165 "version": "4.1.5",
@@ -2253,6 +2296,14 @@
2296 "@types/lodash": "*"
2297 }
2298 },
2299 + "node_modules/@types/mdast": {
2300 + "version": "4.0.4",
2301 + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
2302 + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==",
2303 + "dependencies": {
2304 + "@types/unist": "*"
2305 + }
2306 + },
2307 "node_modules/@types/node": {
2308 "version": "22.5.4",
2309 "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz",
@@ -2817,6 +2868,11 @@
2868 "url": "https://opencollective.com/typescript-eslint"
2869 }
2870 },
2871 + "node_modules/@ungap/structured-clone": {
2872 + "version": "1.2.0",
2873 + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
2874 + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ=="
2875 + },
2876 "node_modules/@unocss/config": {
2877 "version": "0.56.5",
2878 "resolved": "https://registry.npmjs.org/@unocss/config/-/config-0.56.5.tgz",
@@ -3093,36 +3149,36 @@
3149 }
3150 },
3151 "node_modules/@vue/compiler-core": {
3096 - "version": "3.5.3",
3097 - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.3.tgz",
3098 - "integrity": "sha512-adAfy9boPkP233NTyvLbGEqVuIfK/R0ZsBsIOW4BZNfb4BRpRW41Do1u+ozJpsb+mdoy80O20IzAsHaihRb5qA==",
3152 + "version": "3.5.4",
3153 + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.4.tgz",
3154 + "integrity": "sha512-oNwn+BAt3n9dK9uAYvI+XGlutwuTq/wfj4xCBaZCqwwVIGtD7D6ViihEbyYZrDHIHTDE3Q6oL3/hqmAyFEy9DQ==",
3155 "dependencies": {
3156 "@babel/parser": "^7.25.3",
3101 - "@vue/shared": "3.5.3",
3157 + "@vue/shared": "3.5.4",
3158 "entities": "^4.5.0",
3159 "estree-walker": "^2.0.2",
3160 "source-map-js": "^1.2.0"
3161 }
3162 },
3163 "node_modules/@vue/compiler-dom": {
3108 - "version": "3.5.3",
3109 - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.3.tgz",
3110 - "integrity": "sha512-wnzFArg9zpvk/811CDOZOadJRugf1Bgl/TQ3RfV4nKfSPok4hi0w10ziYUQR6LnnBAUlEXYLUfZ71Oj9ds/+QA==",
3164 + "version": "3.5.4",
3165 + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.4.tgz",
3166 + "integrity": "sha512-yP9RRs4BDLOLfldn6ah+AGCNovGjMbL9uHvhDHf5wan4dAHLnFGOkqtfE7PPe4HTXIqE7l/NILdYw53bo1C8jw==",
3167 "dependencies": {
3112 - "@vue/compiler-core": "3.5.3",
3113 - "@vue/shared": "3.5.3"
3168 + "@vue/compiler-core": "3.5.4",
3169 + "@vue/shared": "3.5.4"
3170 }
3171 },
3172 "node_modules/@vue/compiler-sfc": {
3117 - "version": "3.5.3",
3118 - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.3.tgz",
3119 - "integrity": "sha512-P3uATLny2tfyvMB04OQFe7Sczteno7SLFxwrOA/dw01pBWQHB5HL15a8PosoNX2aG/EAMGqnXTu+1LnmzFhpTQ==",
3173 + "version": "3.5.4",
3174 + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.4.tgz",
3175 + "integrity": "sha512-P+yiPhL+NYH7m0ZgCq7AQR2q7OIE+mpAEgtkqEeH9oHSdIRvUO+4X6MPvblJIWcoe4YC5a2Gdf/RsoyP8FFiPQ==",
3176 "dependencies": {
3177 "@babel/parser": "^7.25.3",
3122 - "@vue/compiler-core": "3.5.3",
3123 - "@vue/compiler-dom": "3.5.3",
3124 - "@vue/compiler-ssr": "3.5.3",
3125 - "@vue/shared": "3.5.3",
3178 + "@vue/compiler-core": "3.5.4",
3179 + "@vue/compiler-dom": "3.5.4",
3180 + "@vue/compiler-ssr": "3.5.4",
3181 + "@vue/shared": "3.5.4",
3182 "estree-walker": "^2.0.2",
3183 "magic-string": "^0.30.11",
3184 "postcss": "^8.4.44",
@@ -3130,12 +3186,12 @@
3186 }
3187 },
3188 "node_modules/@vue/compiler-ssr": {
3133 - "version": "3.5.3",
3134 - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.3.tgz",
3135 - "integrity": "sha512-F/5f+r2WzL/2YAPl7UlKcJWHrvoZN8XwEBLnT7S4BXwncH25iDOabhO2M2DWioyTguJAGavDOawejkFXj8EM1w==",
3189 + "version": "3.5.4",
3190 + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.4.tgz",
3191 + "integrity": "sha512-acESdTXsxPnYr2C4Blv0ggx5zIFMgOzZmYU2UgvIff9POdRGbRNBHRyzHAnizcItvpgerSKQbllUc9USp3V7eg==",
3192 "dependencies": {
3137 - "@vue/compiler-dom": "3.5.3",
3138 - "@vue/shared": "3.5.3"
3193 + "@vue/compiler-dom": "3.5.4",
3194 + "@vue/shared": "3.5.4"
3195 }
3196 },
3197 "node_modules/@vue/compiler-vue2": {
@@ -3149,18 +3205,18 @@
3205 }
3206 },
3207 "node_modules/@vue/devtools-api": {
3152 - "version": "6.6.3",
3153 - "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.3.tgz",
3154 - "integrity": "sha512-0MiMsFma/HqA6g3KLKn+AGpL1kgKhFWszC9U29NfpWK5LE7bjeXxySWJrOJ77hBz+TBrBQ7o4QJqbPbqbs8rJw=="
3208 + "version": "6.6.4",
3209 + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
3210 + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g=="
3211 },
3212 "node_modules/@vue/devtools-core": {
3157 - "version": "7.4.4",
3158 - "resolved": "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-7.4.4.tgz",
3159 - "integrity": "sha512-DLxgA3DfeADkRzhAfm3G2Rw/cWxub64SdP5b+s5dwL30+whOGj+QNhmyFpwZ8ZTrHDFRIPj0RqNzJ8IRR1pz7w==",
3213 + "version": "7.4.5",
3214 + "resolved": "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-7.4.5.tgz",
3215 + "integrity": "sha512-QwrgKYxwafJUJrKRvJGbzQLuGt0BC1P4qTr4qlEKgOOs+GV0n6zTry2oeHiwmj5H6pOkLoHnwFMOTB9rFtn9QQ==",
3216 "dev": true,
3217 "dependencies": {
3162 - "@vue/devtools-kit": "^7.4.4",
3163 - "@vue/devtools-shared": "^7.4.4",
3218 + "@vue/devtools-kit": "^7.4.5",
3219 + "@vue/devtools-shared": "^7.4.5",
3220 "mitt": "^3.0.1",
3221 "nanoid": "^3.3.4",
3222 "pathe": "^1.1.2",
@@ -3171,12 +3227,12 @@
3227 }
3228 },
3229 "node_modules/@vue/devtools-kit": {
3174 - "version": "7.4.4",
3175 - "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.4.4.tgz",
3176 - "integrity": "sha512-awK/4NfsUG0nQ7qnTM37m7ZkEUMREyPh8taFCX+uQYps/MTFEum0AD05VeGDRMXwWvMmGIcWX9xp8ZiBddY0jw==",
3230 + "version": "7.4.5",
3231 + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.4.5.tgz",
3232 + "integrity": "sha512-Uuki4Z6Bc/ExvtlPkeDNGSAe4580R+HPcVABfTE9TF7BTz3Nntk7vxIRUyWblZkUEcB/x+wn2uofyt5i2LaUew==",
3233 "dev": true,
3234 "dependencies": {
3179 - "@vue/devtools-shared": "^7.4.4",
3235 + "@vue/devtools-shared": "^7.4.5",
3236 "birpc": "^0.2.17",
3237 "hookable": "^5.5.3",
3238 "mitt": "^3.0.1",
@@ -3186,9 +3242,9 @@
3242 }
3243 },
3244 "node_modules/@vue/devtools-shared": {
3189 - "version": "7.4.4",
3190 - "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.4.4.tgz",
3191 - "integrity": "sha512-yeJULXFHOKIm8yL2JFO050a9ztTVqOCKTqN9JHFxGTJN0b+gjtfn6zC+FfyHUgjwCwf6E3hfKrlohtthcqoYqw==",
3245 + "version": "7.4.5",
3246 + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.4.5.tgz",
3247 + "integrity": "sha512-2XgUOkL/7QDmyYI9J7cm+rz/qBhcGv+W5+i1fhwdQ0HQ1RowhdK66F0QBuJSz/5k12opJY8eN6m03/XZMs7imQ==",
3248 "dev": true,
3249 "dependencies": {
3250 "rfdc": "^1.4.1"
@@ -3257,49 +3313,49 @@
3313 }
3314 },
3315 "node_modules/@vue/reactivity": {
3260 - "version": "3.5.3",
3261 - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.3.tgz",
3262 - "integrity": "sha512-2w61UnRWTP7+rj1H/j6FH706gRBHdFVpIqEkSDAyIpafBXYH8xt4gttstbbCWdU3OlcSWO8/3mbKl/93/HSMpw==",
3316 + "version": "3.5.4",
3317 + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.4.tgz",
3318 + "integrity": "sha512-HKKbEuP7tYSGCq4e4nK6ZW6l5hyG66OUetefBp4budUyjvAYsnQDf+bgFzg2RAgnH0CInyqXwD9y47jwJEHrQw==",
3319 "dependencies": {
3264 - "@vue/shared": "3.5.3"
3320 + "@vue/shared": "3.5.4"
3321 }
3322 },
3323 "node_modules/@vue/runtime-core": {
3268 - "version": "3.5.3",
3269 - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.3.tgz",
3270 - "integrity": "sha512-5b2AQw5OZlmCzSsSBWYoZOsy75N4UdMWenTfDdI5bAzXnuVR7iR8Q4AOzQm2OGoA41xjk53VQKrqQhOz2ktWaw==",
3324 + "version": "3.5.4",
3325 + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.4.tgz",
3326 + "integrity": "sha512-f3ek2sTA0AFu0n+w+kCtz567Euqqa3eHewvo4klwS7mWfSj/A+UmYTwsnUFo35KeyAFY60JgrCGvEBsu1n/3LA==",
3327 "dependencies": {
3272 - "@vue/reactivity": "3.5.3",
3273 - "@vue/shared": "3.5.3"
3328 + "@vue/reactivity": "3.5.4",
3329 + "@vue/shared": "3.5.4"
3330 }
3331 },
3332 "node_modules/@vue/runtime-dom": {
3277 - "version": "3.5.3",
3278 - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.3.tgz",
3279 - "integrity": "sha512-wPR1DEGc3XnQ7yHbmkTt3GoY0cEnVGQnARRdAkDzZ8MbUKEs26gogCQo6AOvvgahfjIcnvWJzkZArQ1fmWjcSg==",
3333 + "version": "3.5.4",
3334 + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.4.tgz",
3335 + "integrity": "sha512-ofyc0w6rbD5KtjhP1i9hGOKdxGpvmuB1jprP7Djlj0X7R5J/oLwuNuE98GJ8WW31Hu2VxQHtk/LYTAlW8xrJdw==",
3336 "dependencies": {
3281 - "@vue/reactivity": "3.5.3",
3282 - "@vue/runtime-core": "3.5.3",
3283 - "@vue/shared": "3.5.3",
3337 + "@vue/reactivity": "3.5.4",
3338 + "@vue/runtime-core": "3.5.4",
3339 + "@vue/shared": "3.5.4",
3340 "csstype": "^3.1.3"
3341 }
3342 },
3343 "node_modules/@vue/server-renderer": {
3288 - "version": "3.5.3",
3289 - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.3.tgz",
3290 - "integrity": "sha512-28volmaZVG2PGO3V3+gBPKoSHvLlE8FGfG/GKXKkjjfxLuj/50B/0OQGakM/g6ehQeqCrZYM4eHC4Ks48eig1Q==",
3344 + "version": "3.5.4",
3345 + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.4.tgz",
3346 + "integrity": "sha512-FbjV6DJLgKRetMYFBA1UXCroCiED/Ckr53/ba9wivyd7D/Xw9fpo0T6zXzCnxQwyvkyrL7y6plgYhWhNjGxY5g==",
3347 "dependencies": {
3292 - "@vue/compiler-ssr": "3.5.3",
3293 - "@vue/shared": "3.5.3"
3348 + "@vue/compiler-ssr": "3.5.4",
3349 + "@vue/shared": "3.5.4"
3350 },
3351 "peerDependencies": {
3296 - "vue": "3.5.3"
3352 + "vue": "3.5.4"
3353 }
3354 },
3355 "node_modules/@vue/shared": {
3300 - "version": "3.5.3",
3301 - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.3.tgz",
3302 - "integrity": "sha512-Jp2v8nylKBT+PlOUjun2Wp/f++TfJVFjshLzNtJDdmFJabJa7noGMncqXRM1vXGX+Yo2V7WykQFNxusSim8SCA=="
3356 + "version": "3.5.4",
3357 + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.4.tgz",
3358 + "integrity": "sha512-L2MCDD8l7yC62Te5UUyPVpmexhL9ipVnYRw9CsWfm/BGRL5FwDX4a25bcJ/OJSD3+Hx+k/a8LDKcG2AFdJV3BA=="
3359 },
3360 "node_modules/@vue/test-utils": {
3361 "version": "2.4.6",
@@ -4140,6 +4196,15 @@
4196 "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==",
4197 "dev": true
4198 },
4199 + "node_modules/ccount": {
4200 + "version": "2.0.1",
4201 + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz",
4202 + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==",
4203 + "funding": {
4204 + "type": "github",
4205 + "url": "https://github.com/sponsors/wooorm"
4206 + }
4207 + },
4208 "node_modules/chai": {
4209 "version": "5.1.1",
4210 "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.1.tgz",
@@ -4169,6 +4234,24 @@
4234 "node": ">=4"
4235 }
4236 },
4237 + "node_modules/character-entities-html4": {
4238 + "version": "2.1.0",
4239 + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz",
4240 + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==",
4241 + "funding": {
4242 + "type": "github",
4243 + "url": "https://github.com/sponsors/wooorm"
4244 + }
4245 + },
4246 + "node_modules/character-entities-legacy": {
4247 + "version": "3.0.0",
4248 + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
4249 + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
4250 + "funding": {
4251 + "type": "github",
4252 + "url": "https://github.com/sponsors/wooorm"
4253 + }
4254 + },
4255 "node_modules/check-error": {
4256 "version": "2.1.1",
4257 "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz",
@@ -4418,6 +4501,15 @@
4501 "node": ">= 0.8"
4502 }
4503 },
4504 + "node_modules/comma-separated-tokens": {
4505 + "version": "2.0.3",
4506 + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
4507 + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==",
4508 + "funding": {
4509 + "type": "github",
4510 + "url": "https://github.com/sponsors/wooorm"
4511 + }
4512 + },
4513 "node_modules/commander": {
4514 "version": "6.2.1",
4515 "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
@@ -4903,11 +4995,11 @@
4995 "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="
4996 },
4997 "node_modules/debug": {
4906 - "version": "4.3.6",
4907 - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
4908 - "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==",
4998 + "version": "4.3.7",
4999 + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
5000 + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
5001 "dependencies": {
4910 - "ms": "2.1.2"
5002 + "ms": "^2.1.3"
5003 },
5004 "engines": {
5005 "node": ">=6.0"
@@ -5031,6 +5123,14 @@
5123 "node": ">= 0.8"
5124 }
5125 },
5126 + "node_modules/dequal": {
5127 + "version": "2.0.3",
5128 + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
5129 + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
5130 + "engines": {
5131 + "node": ">=6"
5132 + }
5133 + },
5134 "node_modules/destr": {
5135 "version": "2.0.3",
5136 "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.3.tgz",
@@ -5051,6 +5151,18 @@
5151 "resolved": "https://registry.npmjs.org/detect-touch-device/-/detect-touch-device-1.1.6.tgz",
5152 "integrity": "sha512-9DYLJE05EFGI9f8m/GyJtWjw7aMZMBQM2QVy6bb7zX8uC3iorOE3Erdrk3TWCj5FVOlHTfinU0bATTE9GWYebw=="
5153 },
5154 + "node_modules/devlop": {
5155 + "version": "1.1.0",
5156 + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
5157 + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
5158 + "dependencies": {
5159 + "dequal": "^2.0.0"
5160 + },
5161 + "funding": {
5162 + "type": "github",
5163 + "url": "https://github.com/sponsors/wooorm"
5164 + }
5165 + },
5166 "node_modules/didyoumean": {
5167 "version": "1.2.2",
5168 "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
@@ -5384,15 +5496,16 @@
5496 }
5497 },
5498 "node_modules/eslint": {
5387 - "version": "9.9.1",
5388 - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.9.1.tgz",
5389 - "integrity": "sha512-dHvhrbfr4xFQ9/dq+jcVneZMyRYLjggWjk6RVsIiHsP8Rz6yZ8LvZ//iU4TrZF+SXWG+JkNF2OyiZRvzgRDqMg==",
5499 + "version": "9.10.0",
5500 + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.10.0.tgz",
5501 + "integrity": "sha512-Y4D0IgtBZfOcOUAIQTSXBKoNGfY0REGqHJG6+Q81vNippW5YlKjHFj4soMxamKK1NXHUWuBZTLdU3Km+L/pcHw==",
5502 "dependencies": {
5503 "@eslint-community/eslint-utils": "^4.2.0",
5504 "@eslint-community/regexpp": "^4.11.0",
5505 "@eslint/config-array": "^0.18.0",
5506 "@eslint/eslintrc": "^3.1.0",
5395 - "@eslint/js": "9.9.1",
5507 + "@eslint/js": "9.10.0",
5508 + "@eslint/plugin-kit": "^0.1.0",
5509 "@humanwhocodes/module-importer": "^1.0.1",
5510 "@humanwhocodes/retry": "^0.3.0",
5511 "@nodelib/fs.walk": "^1.2.8",
@@ -5415,7 +5528,6 @@
5528 "is-glob": "^4.0.0",
5529 "is-path-inside": "^3.0.3",
5530 "json-stable-stringify-without-jsonify": "^1.0.1",
5418 - "levn": "^0.4.1",
5531 "lodash.merge": "^4.6.2",
5532 "minimatch": "^3.1.2",
5533 "natural-compare": "^1.4.0",
@@ -5633,9 +5745,9 @@
5745 }
5746 },
5747 "node_modules/eslint/node_modules/@eslint/js": {
5636 - "version": "9.9.1",
5637 - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.1.tgz",
5638 - "integrity": "sha512-xIDQRsfg5hNBqHz04H1R3scSVwmI+KUbqjsQKHKQ1DAUSaUjYPReZZmS/5PNiKu1fUvzDd6H7DEDKACSEhu+TQ==",
5748 + "version": "9.10.0",
5749 + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.10.0.tgz",
5750 + "integrity": "sha512-fuXtbiP5GWIn8Fz+LWoOMVf/Jxm+aajZYkhi6CuEm4SxymFM+eUWzbO9qXT+L0iCkL5+KGYMCSGxo686H19S1g==",
5751 "engines": {
5752 "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
5753 }
@@ -6519,6 +6631,40 @@
6631 "node": ">= 0.4"
6632 }
6633 },
6634 + "node_modules/hast-util-to-html": {
6635 + "version": "9.0.2",
6636 + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.2.tgz",
6637 + "integrity": "sha512-RP5wNpj5nm1Z8cloDv4Sl4RS8jH5HYa0v93YB6Wb4poEzgMo/dAAL0KcT4974dCjcNG5pkLqTImeFHHCwwfY3g==",
6638 + "dependencies": {
6639 + "@types/hast": "^3.0.0",
6640 + "@types/unist": "^3.0.0",
6641 + "ccount": "^2.0.0",
6642 + "comma-separated-tokens": "^2.0.0",
6643 + "hast-util-whitespace": "^3.0.0",
6644 + "html-void-elements": "^3.0.0",
6645 + "mdast-util-to-hast": "^13.0.0",
6646 + "property-information": "^6.0.0",
6647 + "space-separated-tokens": "^2.0.0",
6648 + "stringify-entities": "^4.0.0",
6649 + "zwitch": "^2.0.4"
6650 + },
6651 + "funding": {
6652 + "type": "opencollective",
6653 + "url": "https://opencollective.com/unified"
6654 + }
6655 + },
6656 + "node_modules/hast-util-whitespace": {
6657 + "version": "3.0.0",
6658 + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz",
6659 + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==",
6660 + "dependencies": {
6661 + "@types/hast": "^3.0.0"
6662 + },
6663 + "funding": {
6664 + "type": "opencollective",
6665 + "url": "https://opencollective.com/unified"
6666 + }
6667 + },
6668 "node_modules/he": {
6669 "version": "1.2.0",
6670 "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
@@ -6603,6 +6749,15 @@
6749 "url": "https://github.com/sponsors/sindresorhus"
6750 }
6751 },
6752 + "node_modules/html-void-elements": {
6753 + "version": "3.0.0",
6754 + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz",
6755 + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==",
6756 + "funding": {
6757 + "type": "github",
6758 + "url": "https://github.com/sponsors/wooorm"
6759 + }
6760 + },
6761 "node_modules/http-assert": {
6762 "version": "1.5.0",
6763 "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.5.0.tgz",
@@ -8023,6 +8178,26 @@
8178 "markdown-it": "bin/markdown-it.mjs"
8179 }
8180 },
8181 + "node_modules/mdast-util-to-hast": {
8182 + "version": "13.2.0",
8183 + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz",
8184 + "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==",
8185 + "dependencies": {
8186 + "@types/hast": "^3.0.0",
8187 + "@types/mdast": "^4.0.0",
8188 + "@ungap/structured-clone": "^1.0.0",
8189 + "devlop": "^1.0.0",
8190 + "micromark-util-sanitize-uri": "^2.0.0",
8191 + "trim-lines": "^3.0.0",
8192 + "unist-util-position": "^5.0.0",
8193 + "unist-util-visit": "^5.0.0",
8194 + "vfile": "^6.0.0"
8195 + },
8196 + "funding": {
8197 + "type": "opencollective",
8198 + "url": "https://opencollective.com/unified"
8199 + }
8200 + },
8201 "node_modules/mdn-data": {
8202 "version": "2.0.30",
8203 "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
@@ -8074,6 +8249,90 @@
8249 "node": ">= 0.6"
8250 }
8251 },
8252 + "node_modules/micromark-util-character": {
8253 + "version": "2.1.0",
8254 + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz",
8255 + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==",
8256 + "funding": [
8257 + {
8258 + "type": "GitHub Sponsors",
8259 + "url": "https://github.com/sponsors/unifiedjs"
8260 + },
8261 + {
8262 + "type": "OpenCollective",
8263 + "url": "https://opencollective.com/unified"
8264 + }
8265 + ],
8266 + "dependencies": {
8267 + "micromark-util-symbol": "^2.0.0",
8268 + "micromark-util-types": "^2.0.0"
8269 + }
8270 + },
8271 + "node_modules/micromark-util-encode": {
8272 + "version": "2.0.0",
8273 + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz",
8274 + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==",
8275 + "funding": [
8276 + {
8277 + "type": "GitHub Sponsors",
8278 + "url": "https://github.com/sponsors/unifiedjs"
8279 + },
8280 + {
8281 + "type": "OpenCollective",
8282 + "url": "https://opencollective.com/unified"
8283 + }
8284 + ]
8285 + },
8286 + "node_modules/micromark-util-sanitize-uri": {
8287 + "version": "2.0.0",
8288 + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz",
8289 + "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==",
8290 + "funding": [
8291 + {
8292 + "type": "GitHub Sponsors",
8293 + "url": "https://github.com/sponsors/unifiedjs"
8294 + },
8295 + {
8296 + "type": "OpenCollective",
8297 + "url": "https://opencollective.com/unified"
8298 + }
8299 + ],
8300 + "dependencies": {
8301 + "micromark-util-character": "^2.0.0",
8302 + "micromark-util-encode": "^2.0.0",
8303 + "micromark-util-symbol": "^2.0.0"
8304 + }
8305 + },
8306 + "node_modules/micromark-util-symbol": {
8307 + "version": "2.0.0",
8308 + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz",
8309 + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==",
8310 + "funding": [
8311 + {
8312 + "type": "GitHub Sponsors",
8313 + "url": "https://github.com/sponsors/unifiedjs"
8314 + },
8315 + {
8316 + "type": "OpenCollective",
8317 + "url": "https://opencollective.com/unified"
8318 + }
8319 + ]
8320 + },
8321 + "node_modules/micromark-util-types": {
8322 + "version": "2.0.0",
8323 + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz",
8324 + "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==",
8325 + "funding": [
8326 + {
8327 + "type": "GitHub Sponsors",
8328 + "url": "https://github.com/sponsors/unifiedjs"
8329 + },
8330 + {
8331 + "type": "OpenCollective",
8332 + "url": "https://opencollective.com/unified"
8333 + }
8334 + ]
8335 + },
8336 "node_modules/micromatch": {
8337 "version": "4.0.8",
8338 "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
@@ -8339,9 +8598,9 @@
8598 }
8599 },
8600 "node_modules/ms": {
8342 - "version": "2.1.2",
8343 - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
8344 - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
8601 + "version": "2.1.3",
8602 + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
8603 + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
8604 },
8605 "node_modules/muggle-string": {
8606 "version": "0.4.1",
@@ -8796,6 +9055,14 @@
9055 "url": "https://github.com/sponsors/sindresorhus"
9056 }
9057 },
9058 + "node_modules/oniguruma-to-js": {
9059 + "version": "0.3.3",
9060 + "resolved": "https://registry.npmjs.org/oniguruma-to-js/-/oniguruma-to-js-0.3.3.tgz",
9061 + "integrity": "sha512-m90/WEhgs8g4BxG37+Nu3YrMfJDs2YXtYtIllhsEPR+wP3+K4EZk6dDUvy2v2K4MNFDDOYKL4/yqYPXDqyozTQ==",
9062 + "funding": {
9063 + "url": "https://github.com/sponsors/antfu"
9064 + }
9065 + },
9066 "node_modules/only": {
9067 "version": "0.0.2",
9068 "resolved": "https://registry.npmjs.org/only/-/only-0.0.2.tgz",
@@ -8995,10 +9262,13 @@
9262 "dev": true
9263 },
9264 "node_modules/path-to-regexp": {
8998 - "version": "6.2.2",
8999 - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.2.tgz",
9000 - "integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==",
9001 - "dev": true
9265 + "version": "8.1.0",
9266 + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.1.0.tgz",
9267 + "integrity": "sha512-Bqn3vc8CMHty6zuD+tG23s6v2kwxslHEhTj4eYaVKGIEB+YX/2wd0/rgXLFD9G9id9KCtbVy/3ZgmvZjpa0UdQ==",
9268 + "dev": true,
9269 + "engines": {
9270 + "node": ">=16"
9271 + }
9272 },
9273 "node_modules/path-type": {
9274 "version": "4.0.0",
@@ -9111,9 +9381,9 @@
9381 }
9382 },
9383 "node_modules/pinia-plugin-persistedstate": {
9114 - "version": "4.0.0",
9115 - "resolved": "https://registry.npmjs.org/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-4.0.0.tgz",
9116 - "integrity": "sha512-yTly/5XMwgd4hQmZOTAdeK3Kfz89h9TMS2/d0VzVJEL9v2LanZdjhckpwnE+jelL+vsT7DnRLmSOtkzSJjO6Ng==",
9384 + "version": "4.0.1",
9385 + "resolved": "https://registry.npmjs.org/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-4.0.1.tgz",
9386 + "integrity": "sha512-Gs2nhKUi6Rwhhc8PH9wvEAxfz4iZeJ/36qQv2ieYs94+zMozbGtMLIR4p9Aoim3c/PKf8J9orOz9n+f0ZF9CdA==",
9387 "dependencies": {
9388 "@nuxt/kit": "^3.13.0",
9389 "deep-pick-omit": "^1.2.0",
@@ -9438,6 +9708,15 @@
9708 "node": ">=10"
9709 }
9710 },
9711 + "node_modules/property-information": {
9712 + "version": "6.5.0",
9713 + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz",
9714 + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==",
9715 + "funding": {
9716 + "type": "github",
9717 + "url": "https://github.com/sponsors/wooorm"
9718 + }
9719 + },
9720 "node_modules/proto-list": {
9721 "version": "1.2.4",
9722 "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
@@ -9584,6 +9863,11 @@
9863 "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
9864 "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
9865 },
9866 + "node_modules/regex": {
9867 + "version": "4.3.2",
9868 + "resolved": "https://registry.npmjs.org/regex/-/regex-4.3.2.tgz",
9869 + "integrity": "sha512-kK/AA3A9K6q2js89+VMymcboLOlF5lZRCYJv3gzszXFHBr6kO6qLGzbm+UIugBEV8SMMKCTR59txoY6ctRHYVw=="
9870 + },
9871 "node_modules/replace-in-file": {
9872 "version": "6.3.5",
9873 "resolved": "https://registry.npmjs.org/replace-in-file/-/replace-in-file-6.3.5.tgz",
@@ -10147,12 +10431,13 @@
10431 }
10432 },
10433 "node_modules/shiki": {
10150 - "version": "1.16.2",
10151 - "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.16.2.tgz",
10152 - "integrity": "sha512-gSym0hZf5a1U0iDPsdoOAZbvoi+e0c6c3NKAi03FoSLTm7oG20tum29+gk0wzzivOasn3loxfGUPT+jZXIUbWg==",
10434 + "version": "1.17.0",
10435 + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.17.0.tgz",
10436 + "integrity": "sha512-VZf8cPShRwfzPcaswv81+YP7qJEoFwRT+Ehy6bizim7M0zG9bk8Egug550C+xS9g7rKIOPhzAlp2uEyuCxbk/A==",
10437 "dependencies": {
10154 - "@shikijs/core": "1.16.2",
10155 - "@shikijs/vscode-textmate": "^9.2.0",
10438 + "@shikijs/core": "1.17.0",
10439 + "@shikijs/types": "1.17.0",
10440 + "@shikijs/vscode-textmate": "^9.2.2",
10441 "@types/hast": "^3.0.4"
10442 }
10443 },
@@ -10321,6 +10606,15 @@
10606 "node": ">=0.10.0"
10607 }
10608 },
10609 + "node_modules/space-separated-tokens": {
10610 + "version": "2.0.2",
10611 + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
10612 + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==",
10613 + "funding": {
10614 + "type": "github",
10615 + "url": "https://github.com/sponsors/wooorm"
10616 + }
10617 + },
10618 "node_modules/speakingurl": {
10619 "version": "14.0.1",
10620 "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz",
@@ -10392,19 +10686,19 @@
10686 "dev": true
10687 },
10688 "node_modules/start-server-and-test": {
10395 - "version": "2.0.5",
10396 - "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-2.0.5.tgz",
10397 - "integrity": "sha512-2CV4pz69NJVJKQmJeSr+O+SPtOreu0yxvhPmSXclzmAKkPREuMabyMh+Txpzemjx0RDzXOcG2XkhiUuxjztSQw==",
10689 + "version": "2.0.7",
10690 + "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-2.0.7.tgz",
10691 + "integrity": "sha512-tsOG1QbXKcRmrhCT0SrmmTex+uTjrb0SG8iF9XFmw1Cu8vPbVhER8zR4xkm5LAt1BarM/Dk7Jkfts+h8Rxfb2w==",
10692 "dev": true,
10693 "dependencies": {
10694 "arg": "^5.0.2",
10695 "bluebird": "3.7.2",
10696 "check-more-types": "2.24.0",
10403 - "debug": "4.3.6",
10697 + "debug": "4.3.7",
10698 "execa": "5.1.1",
10699 "lazy-ass": "1.6.0",
10700 "ps-tree": "1.2.0",
10407 - "wait-on": "7.2.0"
10701 + "wait-on": "8.0.0"
10702 },
10703 "bin": {
10704 "server-test": "src/bin/start.js",
@@ -10511,6 +10805,19 @@
10805 "node": ">=8"
10806 }
10807 },
10808 + "node_modules/stringify-entities": {
10809 + "version": "4.0.4",
10810 + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
10811 + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==",
10812 + "dependencies": {
10813 + "character-entities-html4": "^2.0.0",
10814 + "character-entities-legacy": "^3.0.0"
10815 + },
10816 + "funding": {
10817 + "type": "github",
10818 + "url": "https://github.com/sponsors/wooorm"
10819 + }
10820 + },
10821 "node_modules/strip-ansi": {
10822 "version": "6.0.1",
10823 "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
@@ -11129,6 +11436,15 @@
11436 "resolved": "https://registry.npmjs.org/treemate/-/treemate-0.3.11.tgz",
11437 "integrity": "sha512-M8RGFoKtZ8dF+iwJfAJTOH/SM4KluKOKRJpjCMhI8bG3qB74zrFoArKZ62ll0Fr3mqkMJiQOmWYkdYgDeITYQg=="
11438 },
11439 + "node_modules/trim-lines": {
11440 + "version": "3.0.1",
11441 + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
11442 + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==",
11443 + "funding": {
11444 + "type": "github",
11445 + "url": "https://github.com/sponsors/wooorm"
11446 + }
11447 + },
11448 "node_modules/ts-api-utils": {
11449 "version": "1.3.0",
11450 "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
@@ -11209,9 +11525,9 @@
11525 }
11526 },
11527 "node_modules/type-fest": {
11212 - "version": "4.26.0",
11213 - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.0.tgz",
11214 - "integrity": "sha512-OduNjVJsFbifKb57UqZ2EMP1i4u64Xwow3NYXUtBbD4vIwJdQd4+xl8YDou1dlm4DVrtwT/7Ky8z8WyCULVfxw==",
11528 + "version": "4.26.1",
11529 + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.1.tgz",
11530 + "integrity": "sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==",
11531 "dev": true,
11532 "engines": {
11533 "node": ">=16"
@@ -11373,6 +11689,69 @@
11689 "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
11690 }
11691 },
11692 + "node_modules/unist-util-is": {
11693 + "version": "6.0.0",
11694 + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz",
11695 + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==",
11696 + "dependencies": {
11697 + "@types/unist": "^3.0.0"
11698 + },
11699 + "funding": {
11700 + "type": "opencollective",
11701 + "url": "https://opencollective.com/unified"
11702 + }
11703 + },
11704 + "node_modules/unist-util-position": {
11705 + "version": "5.0.0",
11706 + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz",
11707 + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==",
11708 + "dependencies": {
11709 + "@types/unist": "^3.0.0"
11710 + },
11711 + "funding": {
11712 + "type": "opencollective",
11713 + "url": "https://opencollective.com/unified"
11714 + }
11715 + },
11716 + "node_modules/unist-util-stringify-position": {
11717 + "version": "4.0.0",
11718 + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
11719 + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
11720 + "dependencies": {
11721 + "@types/unist": "^3.0.0"
11722 + },
11723 + "funding": {
11724 + "type": "opencollective",
11725 + "url": "https://opencollective.com/unified"
11726 + }
11727 + },
11728 + "node_modules/unist-util-visit": {
11729 + "version": "5.0.0",
11730 + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz",
11731 + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==",
11732 + "dependencies": {
11733 + "@types/unist": "^3.0.0",
11734 + "unist-util-is": "^6.0.0",
11735 + "unist-util-visit-parents": "^6.0.0"
11736 + },
11737 + "funding": {
11738 + "type": "opencollective",
11739 + "url": "https://opencollective.com/unified"
11740 + }
11741 + },
11742 + "node_modules/unist-util-visit-parents": {
11743 + "version": "6.0.1",
11744 + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz",
11745 + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==",
11746 + "dependencies": {
11747 + "@types/unist": "^3.0.0",
11748 + "unist-util-is": "^6.0.0"
11749 + },
11750 + "funding": {
11751 + "type": "opencollective",
11752 + "url": "https://opencollective.com/unified"
11753 + }
11754 + },
11755 "node_modules/universalify": {
11756 "version": "2.0.1",
11757 "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
@@ -11592,10 +11971,36 @@
11971 "extsprintf": "^1.2.0"
11972 }
11973 },
11974 + "node_modules/vfile": {
11975 + "version": "6.0.3",
11976 + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
11977 + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==",
11978 + "dependencies": {
11979 + "@types/unist": "^3.0.0",
11980 + "vfile-message": "^4.0.0"
11981 + },
11982 + "funding": {
11983 + "type": "opencollective",
11984 + "url": "https://opencollective.com/unified"
11985 + }
11986 + },
11987 + "node_modules/vfile-message": {
11988 + "version": "4.0.2",
11989 + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz",
11990 + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==",
11991 + "dependencies": {
11992 + "@types/unist": "^3.0.0",
11993 + "unist-util-stringify-position": "^4.0.0"
11994 + },
11995 + "funding": {
11996 + "type": "opencollective",
11997 + "url": "https://opencollective.com/unified"
11998 + }
11999 + },
12000 "node_modules/vite": {
11596 - "version": "5.4.3",
11597 - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.3.tgz",
11598 - "integrity": "sha512-IH+nl64eq9lJjFqU+/yrRnrHPVTlgy42/+IzbOdaFDVlyLgI/wDlf+FCobXLX1cT0X5+7LMyH1mIy2xJdLfo8Q==",
12001 + "version": "5.4.4",
12002 + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.4.tgz",
12003 + "integrity": "sha512-RHFCkULitycHVTtelJ6jQLd+KSAAzOgEYorV32R2q++M6COBjKJR6BxqClwp5sf0XaBDjVMuJ9wnNfyAJwjMkA==",
12004 "dev": true,
12005 "dependencies": {
12006 "esbuild": "^0.21.3",
@@ -11786,14 +12191,14 @@
12191 }
12192 },
12193 "node_modules/vite-plugin-vue-devtools": {
11789 - "version": "7.4.4",
11790 - "resolved": "https://registry.npmjs.org/vite-plugin-vue-devtools/-/vite-plugin-vue-devtools-7.4.4.tgz",
11791 - "integrity": "sha512-lJ7Vr6gznv1nf2S75XJTpXl4XcwnHfyvqJQ7szOvTUfumQALDGo772TEH69wx8gkY/ZWZQea4DZR5IQZMOZKUA==",
12194 + "version": "7.4.5",
12195 + "resolved": "https://registry.npmjs.org/vite-plugin-vue-devtools/-/vite-plugin-vue-devtools-7.4.5.tgz",
12196 + "integrity": "sha512-8D9Dv7uw620r96uI45M7hCyfsBA9KwttdmxA/DAd0/1KpuGmrFEhNwo4ob/sjaou1noAeK2TrnKdW+sFMR6aKQ==",
12197 "dev": true,
12198 "dependencies": {
11794 - "@vue/devtools-core": "^7.4.4",
11795 - "@vue/devtools-kit": "^7.4.4",
11796 - "@vue/devtools-shared": "^7.4.4",
12199 + "@vue/devtools-core": "^7.4.5",
12200 + "@vue/devtools-kit": "^7.4.5",
12201 + "@vue/devtools-shared": "^7.4.5",
12202 "execa": "^8.0.1",
12203 "sirv": "^2.0.4",
12204 "vite-plugin-inspect": "^0.8.7",
@@ -12188,15 +12593,15 @@
12593 "dev": true
12594 },
12595 "node_modules/vue": {
12191 - "version": "3.5.3",
12192 - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.3.tgz",
12193 - "integrity": "sha512-xvRbd0HpuLovYbOHXRHlSBsSvmUJbo0pzbkKTApWnQGf3/cu5Z39mQeA5cZdLRVIoNf3zI6MSoOgHUT5i2jO+Q==",
12596 + "version": "3.5.4",
12597 + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.4.tgz",
12598 + "integrity": "sha512-3yAj2gkmiY+i7+22A1PWM+kjOVXjU74UPINcTiN7grIVPyFFI0lpGwHlV/4xydDmobaBn7/xmi+YG8HeSlCTcg==",
12599 "dependencies": {
12195 - "@vue/compiler-dom": "3.5.3",
12196 - "@vue/compiler-sfc": "3.5.3",
12197 - "@vue/runtime-dom": "3.5.3",
12198 - "@vue/server-renderer": "3.5.3",
12199 - "@vue/shared": "3.5.3"
12600 + "@vue/compiler-dom": "3.5.4",
12601 + "@vue/compiler-sfc": "3.5.4",
12602 + "@vue/runtime-dom": "3.5.4",
12603 + "@vue/server-renderer": "3.5.4",
12604 + "@vue/shared": "3.5.4"
12605 },
12606 "peerDependencies": {
12607 "typescript": "*"
@@ -12296,12 +12701,12 @@
12701 }
12702 },
12703 "node_modules/vue-i18n": {
12299 - "version": "9.14.0",
12300 - "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.14.0.tgz",
12301 - "integrity": "sha512-LxmpRuCt2rI8gqU+kxeflRZMQn4D5+4M3oP3PWZdowW/ePJraHqhF7p4CuaME52mUxdw3Mmy2yAUKgfZYgCRjA==",
12704 + "version": "10.0.0",
12705 + "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-10.0.0.tgz",
12706 + "integrity": "sha512-KxTfTEuZEGN5Bvgc9F49rgp94XyBFlSIszwF2SQlr3WoxOklySXdUuoVxIw5qPZthV0mJlGP8tjJR7loMJgKrQ==",
12707 "dependencies": {
12303 - "@intlify/core-base": "9.14.0",
12304 - "@intlify/shared": "9.14.0",
12708 + "@intlify/core-base": "10.0.0",
12709 + "@intlify/shared": "10.0.0",
12710 "@vue/devtools-api": "^6.5.0"
12711 },
12712 "engines": {
@@ -12315,11 +12720,11 @@
12720 }
12721 },
12722 "node_modules/vue-router": {
12318 - "version": "4.4.3",
12319 - "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.4.3.tgz",
12320 - "integrity": "sha512-sv6wmNKx2j3aqJQDMxLFzs/u/mjA9Z5LCgy6BE0f7yFWMjrPLnS/sPNn8ARY/FXw6byV18EFutn5lTO6+UsV5A==",
12723 + "version": "4.4.4",
12724 + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.4.4.tgz",
12725 + "integrity": "sha512-3MlnDqwRwZwCQVbtVfpsU+nrNymNjnXSsQtXName5925NVC1+326VVfYH9vSrA0N13teGEo8z5x7gbRnGjCDiQ==",
12726 "dependencies": {
12322 - "@vue/devtools-api": "^6.6.3"
12727 + "@vue/devtools-api": "^6.6.4"
12728 },
12729 "funding": {
12730 "url": "https://github.com/sponsors/posva"
@@ -12423,13 +12828,13 @@
12828 }
12829 },
12830 "node_modules/wait-on": {
12426 - "version": "7.2.0",
12427 - "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-7.2.0.tgz",
12428 - "integrity": "sha512-wCQcHkRazgjG5XoAq9jbTMLpNIjoSlZslrJ2+N9MxDsGEv1HnFoVjOCexL0ESva7Y9cu350j+DWADdk54s4AFQ==",
12831 + "version": "8.0.0",
12832 + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-8.0.0.tgz",
12833 + "integrity": "sha512-fNE5SXinLr2Bt7cJvjvLg2PcXfqznlqRvtE3f8AqYdRZ9BhE+XpsCp1mwQbRoO7s1q7uhAuCw0Ro3mG/KdZjEw==",
12834 "dev": true,
12835 "dependencies": {
12431 - "axios": "^1.6.1",
12432 - "joi": "^17.11.0",
12836 + "axios": "^1.7.4",
12837 + "joi": "^17.13.3",
12838 "lodash": "^4.17.21",
12839 "minimist": "^1.2.8",
12840 "rxjs": "^7.8.1"
@@ -12798,6 +13203,15 @@
13203 "dependencies": {
13204 "tslib": "2.3.0"
13205 }
13206 + },
13207 + "node_modules/zwitch": {
13208 + "version": "2.0.4",
13209 + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
13210 + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==",
13211 + "funding": {
13212 + "type": "github",
13213 + "url": "https://github.com/sponsors/wooorm"
13214 + }
13215 }
13216 }
13217 }
frontend/package.json
+17 -16
@@ -32,11 +32,11 @@
32 "dependencies": {
33 "@ajoelp/json-to-formdata": "^1.5.0",
34 "@f3ve/vue-markdown-it": "^0.2.2",
35 - "@fontsource/jetbrains-mono": "^5.0.21",
36 - "@fontsource/lexend": "^5.0.21",
35 + "@fontsource/jetbrains-mono": "^5.0.22",
36 + "@fontsource/lexend": "^5.0.22",
37 "@fontsource/public-sans": "^5.0.18",
38 "@popperjs/core": "^2.11.8",
39 - "@shikijs/markdown-it": "^1.16.2",
39 + "@shikijs/markdown-it": "^1.17.0",
40 "@vueuse/components": "^11.0.3",
41 "@vueuse/core": "^11.0.3",
42 "apexcharts": "^3.53.0",
@@ -56,15 +56,15 @@
56 "naive-ui": "^2.39.0",
57 "password-validator": "^5.3.0",
58 "pinia": "^2.2.2",
59 - "pinia-plugin-persistedstate": "^4.0.0",
59 + "pinia-plugin-persistedstate": "^4.0.1",
60 "secure-ls": "^2.0.0",
61 - "shiki": "^1.16.2",
61 + "shiki": "^1.17.0",
62 "validator": "^13.12.0",
63 - "vue": "^3.5.3",
63 + "vue": "^3.5.4",
64 "vue-advanced-cropper": "^2.8.9",
65 "vue-highlight-words": "^3.0.1",
66 - "vue-i18n": "^9.14.0",
67 - "vue-router": "^4.4.3",
66 + "vue-i18n": "^10.0.0",
67 + "vue-router": "^4.4.4",
68 "vue-sjv": "^0.0.6",
69 "vue3-apexcharts": "^1.6.0",
70 "vue3-marquee": "^4.2.2",
@@ -90,7 +90,7 @@
90 "@vue/tsconfig": "^0.5.1",
91 "autoprefixer": "^10.4.20",
92 "cypress": "^13.14.2",
93 - "eslint": "^9.9.1",
93 + "eslint": "^9.10.0",
94 "eslint-plugin-cypress": "^3.5.0",
95 "eslint-plugin-vue": "^9.28.0",
96 "flourite": "^1.3.0",
@@ -103,16 +103,16 @@
103 "postcss": "^8.4.45",
104 "prettier": "^3.3.3",
105 "sass": "^1.78.0",
106 - "start-server-and-test": "^2.0.5",
106 + "start-server-and-test": "^2.0.7",
107 "tailwind-config-viewer": "^2.0.4",
108 "tailwindcss": "^3.4.10",
109 "taze": "^0.16.7",
110 - "type-fest": "^4.26.0",
110 + "type-fest": "^4.26.1",
111 "unplugin-vue-components": "^0.27.4",
112 - "vite": "^5.4.3",
112 + "vite": "^5.4.4",
113 "vite-bundle-analyzer": "^0.10.6",
114 "vite-bundle-visualizer": "^1.2.1",
115 - "vite-plugin-vue-devtools": "^7.4.4",
115 + "vite-plugin-vue-devtools": "^7.4.5",
116 "vite-svg-loader": "^5.1.0",
117 "vitest": "^2.0.5",
118 "vue-tsc": "^2.1.6"
@@ -122,18 +122,19 @@
122 },
123 "overrides": {
124 "@vue/eslint-config-typescript": {
125 - "eslint": "^9.9.1"
125 + "eslint": "^9.10.0"
126 },
127 "@typescript-eslint/type-utils": {
128 - "eslint": "^9.9.1"
128 + "eslint": "^9.10.0"
129 },
130 "@typescript-eslint/utils": {
131 - "eslint": "^9.9.1"
131 + "eslint": "^9.10.0"
132 },
133 "secure-ls": {
134 "crypto-js": "^4.2.0"
135 },
136 "micromatch": "^4.0.8",
137 + "path-to-regexp": "^8.1.0",
138 "ws": "^8.18.0"
139 },
140 "engines": {
frontend/public/images/bitdefender/bitdefender_api_key.png
Binary files /dev/null and b/frontend/public/images/bitdefender/bitdefender_api_key.png differ
frontend/src/api/endpoints/integrations.ts
+17 -7
@@ -54,33 +54,43 @@ export default {
54 office365Provision(customerCode: string, integrationName: string) {
55 return HttpClient.post<FlaskBaseResponse>(`/office365/provision`, {
56 customer_code: customerCode,
57 - integration_name: integrationName
57 + integration_name: integrationName || "Office365"
58 })
59 },
60 mimecastProvision(customerCode: string, integrationName: string) {
61 return HttpClient.post<FlaskBaseResponse>(`/mimecast/provision`, {
62 customer_code: customerCode,
63 - integration_name: integrationName
63 + integration_name: integrationName || "Mimecast"
64 })
65 },
66 crowdstrikeProvision(customerCode: string, integrationName: string) {
67 return HttpClient.post<FlaskBaseResponse>(`/crowdstrike/provision`, {
68 customer_code: customerCode,
69 - integration_name: integrationName
69 + integration_name: integrationName || "Crowdstrike",
70 + hot_data_retention: 30,
71 + index_replicas: 0
72 })
73 },
74 duoProvision(customerCode: string, integrationName: string) {
75 return HttpClient.post<FlaskBaseResponse>(`/duo/provision`, {
76 customer_code: customerCode,
75 - time_interval: 15,
76 - integration_name: integrationName
77 + integration_name: integrationName || "Duo",
78 + time_interval: 15
79 })
80 },
81 darktraceProvision(customerCode: string, integrationName: string) {
82 return HttpClient.post<FlaskBaseResponse>(`/darktrace/provision`, {
83 customer_code: customerCode,
82 - time_interval: 15,
83 - integration_name: integrationName
84 + integration_name: integrationName || "Darktrace",
85 + time_interval: 15
86 + })
87 + },
88 + bitdefenderProvision(customerCode: string, integrationName: string) {
89 + return HttpClient.post<FlaskBaseResponse>(`/bitdefender/provision`, {
90 + customer_code: customerCode,
91 + integration_name: integrationName || "BitDefender",
92 + hot_data_retention: 30,
93 + index_replicas: 0
94 })
95 }
96 // #endregion
frontend/src/components/customers/integrations/CustomerIntegrationActions.vue
+10 -1
@@ -59,9 +59,15 @@ const isMimecast = computed(() => serviceName.value === "Mimecast")
59 const isCrowdstrike = computed(() => serviceName.value === "Crowdstrike")
60 const isDuo = computed(() => serviceName.value === "DUO")
61 const isDarktrace = computed(() => serviceName.value === "Darktrace")
62 +const isBitdefender = computed(() => serviceName.value === "BitDefender")
63 const isDeployEnabled = computed(
64 () =>
64 - (isOffice365.value || isMimecast.value || isCrowdstrike.value || isDuo.value || isDarktrace.value) &&
65 + (isOffice365.value ||
66 + isMimecast.value ||
67 + isCrowdstrike.value ||
68 + isDuo.value ||
69 + isDarktrace.value ||
70 + isBitdefender.value) &&
71 !integration.deployed
72 )
73
@@ -91,6 +97,9 @@ function provision() {
97 if (isDarktrace.value) {
98 apiCall = Api.integrations.darktraceProvision(customerCode.value, serviceName.value)
99 }
100 + if (isBitdefender.value) {
101 + apiCall = Api.integrations.bitdefenderProvision(customerCode.value, serviceName.value)
102 + }
103
104 if (!apiCall) {
105 return