main
vue 249 lines 6.32 KB
Raw
1 <template>
2 <div class="artifacts-quarantine">
3 <div class="header flex items-start justify-end gap-2">
4 <div class="flex grow flex-wrap items-center justify-end gap-2">
5 <div v-if="!hideHostnameField" class="grow basis-56">
6 <n-select
7 v-model:value="filters.hostname"
8 :options="agentHostnameOptions"
9 placeholder="Agent hostname"
10 clearable
11 filterable
12 :disabled="loading"
13 size="small"
14 :loading="loadingAgents"
15 />
16 </div>
17 <div class="grow basis-56">
18 <n-select
19 v-model:value="filters.artifact_name"
20 :options="artifactsOptions"
21 placeholder="Artifact name"
22 clearable
23 :disabled="loading"
24 filterable
25 size="small"
26 :loading="loadingArtifacts"
27 />
28 </div>
29 <div v-if="!hideVelociraptorIdField" class="grow basis-56">
30 <n-input
31 v-model:value="filters.velociraptor_id"
32 placeholder="Velociraptor id"
33 clearable
34 :readonly="loading"
35 size="small"
36 />
37 </div>
38 <div>
39 <n-input-group>
40 <n-select
41 v-model:value="filters.action"
42 :options="actionsOptions"
43 :disabled="loading"
44 size="small"
45 class="w-32!"
46 status="success"
47 />
48 <n-button
49 size="small"
50 type="primary"
51 secondary
52 :loading
53 :disabled="!areFiltersValid"
54 @click="getData()"
55 >
56 <Icon :name="SubmitIcon" />
57 </n-button>
58 </n-input-group>
59 </div>
60 </div>
61 </div>
62 <n-spin :show="loading">
63 <div class="my-7 flex min-h-28 flex-col gap-3">
64 <template v-if="quarantineList.length">
65 <QuarantineItem
66 v-for="quarantine of quarantineList"
67 :key="quarantine.Result + quarantine.Time"
68 :quarantine
69 class="item-appear item-appear-bottom item-appear-005"
70 />
71 </template>
72 <template v-else>
73 <n-empty v-if="!loading" description="No items found" class="h-48 justify-center" />
74 </template>
75 </div>
76 </n-spin>
77 </div>
78 </template>
79
80 <script setup lang="ts">
81 // TODO-FE: refactor
82 import type { QuarantineRequest } from "@/api/endpoints/artifacts"
83 import type { Agent } from "@/types/agents.d"
84 import type { Artifact, QuarantineResult } from "@/types/artifacts.d"
85 import { NButton, NEmpty, NInput, NInputGroup, NSelect, NSpin, useMessage } from "naive-ui"
86 import { computed, nextTick, onBeforeMount, ref, toRefs } from "vue"
87 import Api from "@/api"
88 import Icon from "@/components/common/Icon.vue"
89 import QuarantineItem from "./QuarantineItem.vue"
90
91 const props = defineProps<{
92 hostname?: string
93 agents?: Agent[]
94 artifacts?: Artifact[]
95 hideHostnameField?: boolean
96 hideVelociraptorIdField?: boolean
97 }>()
98
99 const emit = defineEmits<{
100 (e: "loaded-agents", value: Agent[]): void
101 (e: "loaded-artifacts", value: Artifact[]): void
102 (e: "action-performed"): void
103 }>()
104
105 const { hostname, agents, artifacts, hideHostnameField, hideVelociraptorIdField } = toRefs(props)
106
107 const message = useMessage()
108 const loadingAgents = ref(false)
109 const loadingArtifacts = ref(false)
110 const loading = ref(false)
111 const agentsList = ref<Agent[]>([])
112 const artifactsList = ref<Artifact[]>([])
113 const quarantineList = ref<QuarantineResult[]>([])
114
115 const SubmitIcon = "carbon:play"
116
117 const filters = ref<Partial<QuarantineRequest>>({})
118
119 const areFiltersValid = computed(() => {
120 return !!filters.value.artifact_name && !!filters.value.hostname
121 })
122
123 const agentHostnameOptions = computed(() => {
124 if (hostname?.value) {
125 return [{ value: hostname.value, label: hostname.value }]
126 }
127 return (agentsList.value || []).map(o => ({ value: o.hostname, label: o.hostname }))
128 })
129
130 const artifactsOptions = computed(() => {
131 return (artifactsList.value || []).map(o => ({ value: o.name, label: o.name }))
132 })
133
134 const actionsOptions = ref([
135 { label: "Quarantine", value: "quarantine" },
136 { label: "Remove", value: "remove_quarantine" }
137 ])
138
139 function getData() {
140 if (areFiltersValid.value) {
141 loading.value = true
142
143 Api.artifacts
144 .quarantine(filters.value as QuarantineRequest)
145 .then(res => {
146 if (res.data.success) {
147 quarantineList.value = res.data?.results || []
148 emit("action-performed")
149 } else {
150 message.warning(res.data?.message || "An error occurred. Please try again later.")
151 }
152 })
153 .catch(err => {
154 quarantineList.value = []
155
156 message.error(err.response?.data?.message || "An error occurred. Please try again later.")
157 })
158 .finally(() => {
159 loading.value = false
160 })
161 }
162 }
163
164 function getAgents(cb?: (agents: Agent[]) => void) {
165 loadingAgents.value = true
166
167 Api.agents
168 .getAgents()
169 .then(res => {
170 if (res.data.success) {
171 agentsList.value = res.data.agents || []
172
173 if (cb && typeof cb === "function") {
174 cb(agentsList.value)
175 }
176 } else {
177 message.error(res.data?.message || "An error occurred. Please try again later.")
178 }
179 })
180 .catch(err => {
181 message.error(err.response?.data?.message || "An error occurred. Please try again later.")
182 })
183 .finally(() => {
184 loadingAgents.value = false
185 })
186 }
187
188 function getArtifacts(cb?: (artifacts: Artifact[]) => void) {
189 loadingArtifacts.value = true
190
191 Api.artifacts
192 .getAll()
193 .then(res => {
194 if (res.data.success) {
195 artifactsList.value = res.data.artifacts || []
196
197 if (cb && typeof cb === "function") {
198 cb(artifactsList.value)
199 }
200 } else {
201 message.error(res.data?.message || "An error occurred. Please try again later.")
202 }
203 })
204 .catch(err => {
205 message.error(err.response?.data?.message || "An error occurred. Please try again later.")
206 })
207 .finally(() => {
208 loadingArtifacts.value = false
209 })
210 }
211
212 onBeforeMount(() => {
213 artifactsList.value = ["Windows.Remediation.Quarantine", "Linux.Remediation.Quarantine"].map(
214 o => ({ name: o }) as Artifact
215 )
216
217 if (hostname?.value) {
218 filters.value.hostname = hostname.value
219 }
220
221 if (agents?.value?.length && !agentsList.value.length) {
222 agentsList.value = agents.value
223 }
224
225 if (artifacts?.value?.length && !artifactsList.value.length) {
226 artifactsList.value = artifacts.value
227 }
228
229 filters.value.action = actionsOptions.value[0]?.value as QuarantineRequest["action"]
230
231 nextTick(() => {
232 if (!agentsList.value.length && !hostname?.value) {
233 getAgents((agents: Agent[]) => {
234 emit("loaded-agents", agents)
235 })
236 }
237 if (!artifactsList.value.length) {
238 getArtifacts((artifacts: Artifact[]) => {
239 emit("loaded-artifacts", artifacts)
240 })
241 }
242 })
243
244 // MOCK
245 /*
246 quarantineList.value = quarantineResult
247 */
248 })
249 </script>