main
vue 137 lines 3.81 KB
Raw
1 <template>
2 <div>
3 <LicenseFeatureCheck
4 feature="SOCFORTRESS AI"
5 feedback="tooltip"
6 :disabled="disabledLicenseCheck"
7 :force-show-feedback="disabledLicenseCheck && !licenseResponse"
8 @response="
9 (() => {
10 licenseChecked = true
11 licenseResponse = $event
12 })()
13 "
14 @start-loading="licenseChecking = true"
15 @stop-loading="licenseChecking = false"
16 >
17 <n-button
18 :size="size || 'small'"
19 ghost
20 type="primary"
21 :loading="loading || licenseChecking"
22 :disabled="!licenseChecked || !licenseResponse"
23 @click="analysis()"
24 >
25 <template #icon>
26 <Icon :name="AiIcon" />
27 </template>
28 <div class="flex items-center gap-2">
29 <span>Velociraptor Artifact Recommendation</span>
30 <Icon v-if="!licenseResponse && licenseChecked" :name="LockIcon" :size="14" />
31 </div>
32 </n-button>
33 </LicenseFeatureCheck>
34
35 <n-modal
36 v-model:show="showModal"
37 preset="card"
38 content-class="p-0!"
39 :style="{ maxWidth: 'min(710px, 90vw)', minHeight: 'min(500px, 90vh)' }"
40 :bordered="false"
41 title="Velociraptor Artifact Recommendation"
42 segmented
43 >
44 <div v-if="analysisResponse?.general_thoughts" class="p-6 pb-3">
45 {{ analysisResponse.general_thoughts }}
46 </div>
47 <div v-if="analysisResponse?.artifact_recommendations?.length" class="flex flex-col gap-3 p-6">
48 <CardEntity
49 v-for="recommendation of analysisResponse.artifact_recommendations"
50 :key="recommendation.name + recommendation.explanation + recommendation.description"
51 embedded
52 >
53 <template #header>
54 {{ recommendation.name }}
55 </template>
56 <template #default>
57 {{ recommendation.description }}
58 </template>
59 <template #footer>
60 {{ recommendation.explanation }}
61 </template>
62 </CardEntity>
63 </div>
64 <n-empty v-else description="No Recommendations found" class="h-48 justify-center" />
65 </n-modal>
66 </div>
67 </template>
68
69 <script setup lang="ts">
70 import type { ButtonSize } from "naive-ui"
71 import type { AiVelociraptorArtifactRecommendationResponse } from "@/types/threatIntel.d"
72 import { NButton, NEmpty, NModal, useMessage } from "naive-ui"
73 import { ref, watchEffect } from "vue"
74 import Api from "@/api"
75 import CardEntity from "@/components/common/cards/CardEntity.vue"
76 import Icon from "@/components/common/Icon.vue"
77 import LicenseFeatureCheck from "@/components/license/LicenseFeatureCheck.vue"
78
79 const {
80 indexName,
81 indexId,
82 agentId,
83 alertId,
84 forceLicenseResponse = undefined,
85 size
86 } = defineProps<{
87 indexName: string
88 indexId: string
89 agentId: string
90 alertId: number
91 forceLicenseResponse?: boolean
92 size?: ButtonSize
93 }>()
94
95 const LockIcon = "carbon:locked"
96 const AiIcon = "mage:stars-c"
97 const showModal = ref<boolean>(false)
98 const loading = ref<boolean>(false)
99 const message = useMessage()
100 const analysisResponse = ref<AiVelociraptorArtifactRecommendationResponse | null>(null)
101 const licenseChecking = ref(false)
102 const licenseChecked = ref(forceLicenseResponse !== undefined)
103 const licenseResponse = ref(forceLicenseResponse ?? false)
104 const disabledLicenseCheck = ref(forceLicenseResponse !== undefined)
105
106 function openResponse() {
107 showModal.value = true
108 }
109
110 function analysis() {
111 loading.value = true
112
113 Api.threatIntel
114 .aiVelociraptorArtifactRecommendation({ indexName, indexId, agentId, alertId })
115 .then(res => {
116 if (res.data.success) {
117 analysisResponse.value = res.data
118
119 openResponse()
120 } else {
121 message.warning(res.data?.message || "An error occurred. Please try again later.")
122 }
123 })
124 .catch(err => {
125 message.error(err.response?.data?.message || "An error occurred. Please try again later.")
126 })
127 .finally(() => {
128 loading.value = false
129 })
130 }
131
132 watchEffect(() => {
133 licenseResponse.value = forceLicenseResponse ?? false
134 licenseChecked.value = forceLicenseResponse !== undefined
135 disabledLicenseCheck.value = forceLicenseResponse !== undefined
136 })
137 </script>