main
vue 179 lines 5.06 KB
Raw
1 <template>
2 <slot :open-evaluation />
3
4 <n-modal
5 v-model:show="showDetails"
6 preset="card"
7 content-class="p-0!"
8 :style="{ maxWidth: 'min(800px, 90vw)', minHeight: 'min(550px, 90vh)' }"
9 :title="`Process Analysis: ${processName}`"
10 :bordered="false"
11 segmented
12 >
13 <n-spin :show="loading" class="min-h-48">
14 <n-tabs v-if="mcpResponse" type="line" animated :tabs-padding="24">
15 <n-tab-pane
16 v-if="mcpResponse.structured_result?.response"
17 name="Analysis"
18 tab="Analysis"
19 display-directive="show"
20 >
21 <div class="p-6 pt-3">
22 <Markdown :source="mcpResponse.structured_result.response" />
23 </div>
24 </n-tab-pane>
25 <n-tab-pane
26 v-if="mcpResponse.structured_result?.thinking_process"
27 name="ThinkingProcess"
28 tab="Thinking Process"
29 display-directive="show:lazy"
30 >
31 <div class="p-6 pt-3">
32 <Markdown :source="mcpResponse.structured_result.thinking_process" />
33 </div>
34 </n-tab-pane>
35 <!-- Legacy tabs for backward compatibility -->
36 <n-tab-pane
37 v-if="evaluation"
38 name="Overview"
39 tab="Overview"
40 display-directive="show:lazy"
41 class="flex flex-col gap-4 py-8!"
42 >
43 <div class="px-7">
44 <n-card embedded class="overflow-hidden">
45 <div class="flex flex-wrap justify-between gap-8">
46 <n-statistic label="Rank" :value="evaluation.rank" tabular-nums />
47 <n-statistic label="EPS" :value="eps" tabular-nums />
48 <n-statistic label="Host Prevalence" :value="`${evaluation.host_prev}%`" tabular-nums />
49 </div>
50 </n-card>
51 </div>
52
53 <div class="px-7">
54 {{ evaluation.description }}
55 </div>
56 </n-tab-pane>
57 <n-tab-pane v-if="evaluation" name="Intel" tab="Intel" display-directive="show:lazy">
58 <div class="p-6 pt-3">
59 <n-input
60 :value="evaluation.intel"
61 type="textarea"
62 readonly
63 placeholder="Empty"
64 size="large"
65 :autosize="{
66 minRows: 3,
67 maxRows: 18
68 }"
69 />
70 </div>
71 </n-tab-pane>
72 <n-tab-pane v-if="evaluation?.hashes?.length" name="Hashes" tab="Hashes" display-directive="show:lazy">
73 <ListPercentage
74 class="p-6 pt-3"
75 :list="evaluation.hashes"
76 label-key="hash"
77 percentage-key="percentage"
78 />
79 </n-tab-pane>
80 <n-tab-pane
81 v-if="evaluation?.network?.length"
82 name="Network"
83 tab="Network"
84 display-directive="show:lazy"
85 >
86 <ListPercentage
87 class="p-6 pt-3"
88 :list="evaluation.network"
89 label-key="port"
90 percentage-key="usage"
91 />
92 </n-tab-pane>
93 <n-tab-pane
94 v-if="evaluation?.parents?.length"
95 name="Parents"
96 tab="Parents"
97 display-directive="show:lazy"
98 >
99 <ListPercentage
100 class="p-6 pt-3"
101 :list="evaluation.parents"
102 label-key="name"
103 percentage-key="percentage"
104 />
105 </n-tab-pane>
106 <n-tab-pane v-if="evaluation?.paths?.length" name="Paths" tab="Paths" display-directive="show:lazy">
107 <ListPercentage
108 class="p-6 pt-3"
109 :list="evaluation.paths"
110 label-key="directory"
111 percentage-key="percentage"
112 />
113 </n-tab-pane>
114 </n-tabs>
115 <n-empty
116 v-if="!loading && !mcpResponse && !evaluation"
117 description="Process analysis not found"
118 class="h-48 justify-center"
119 />
120 </n-spin>
121 </n-modal>
122 </template>
123
124 <script setup lang="ts">
125 import type { EvaluationData, MCPQueryResponse } from "@/types/threatIntel.d"
126 import _toSafeInteger from "lodash/toSafeInteger"
127 import { NCard, NEmpty, NInput, NModal, NSpin, NStatistic, NTabPane, NTabs, useMessage } from "naive-ui"
128 import { computed, defineAsyncComponent, ref } from "vue"
129 import Api from "@/api"
130
131 const { processName } = defineProps<{
132 processName: string
133 }>()
134
135 const ListPercentage = defineAsyncComponent(() => import("@/components/common/ListPercentage.vue"))
136 const Markdown = defineAsyncComponent(() => import("@/components/common/Markdown.vue"))
137
138 const evaluation = ref<EvaluationData | null>(null)
139 const mcpResponse = ref<MCPQueryResponse | null>(null)
140 const showDetails = ref<boolean>(false)
141 const loading = ref<boolean>(false)
142 const message = useMessage()
143
144 const eps = computed(() => _toSafeInteger(evaluation.value?.eps || 0))
145
146 function openEvaluation() {
147 if (!mcpResponse.value && !evaluation.value) {
148 getEvaluation()
149 }
150 showDetails.value = true
151 }
152
153 function getEvaluation() {
154 loading.value = true
155
156 Api.threatIntel
157 .processNameEvaluation(processName)
158 .then(res => {
159 if (res.data.success) {
160 // Check if response is in new MCP format by checking for structured_result property
161 if ("structured_result" in res.data && res.data.structured_result) {
162 mcpResponse.value = res.data as MCPQueryResponse
163 } else {
164 // Fallback to legacy format
165 const legacyResponse = res.data as { data?: EvaluationData }
166 evaluation.value = legacyResponse?.data || null
167 }
168 } else {
169 message.warning(res.data?.message || "An error occurred. Please try again later.")
170 }
171 })
172 .catch(err => {
173 message.error(err.response?.data?.message || "An error occurred. Please try again later.")
174 })
175 .finally(() => {
176 loading.value = false
177 })
178 }
179 </script>