Skip to content

Commit 9e59dd3

Browse files
committed
♻️ [refactor] XSS 공격 방지를 위한 DOMPurify 설치 및 사용
1 parent 04919bf commit 9e59dd3

File tree

13 files changed

+82
-15
lines changed

13 files changed

+82
-15
lines changed

package-lock.json

Lines changed: 28 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"@tanstack/vue-query": "^5.66.0",
1717
"axios": "^1.7.9",
1818
"chart.js": "^4.4.7",
19+
"dompurify": "^3.2.4",
1920
"js-cookie": "^3.0.5",
2021
"pinia": "^2.3.0",
2122
"tailwind-scrollbar-hide": "^2.0.0",
@@ -27,6 +28,7 @@
2728
},
2829
"devDependencies": {
2930
"@tsconfig/node22": "^22.0.0",
31+
"@types/dompurify": "^3.0.5",
3032
"@types/js-cookie": "^3.0.6",
3133
"@types/node": "^22.10.2",
3234
"@typescript-eslint/eslint-plugin": "^8.20.0",

src/api/admin.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { NewLabelTypes, UserRegistrationApiProps, UserUpdateValue } from '@/types/admin'
22
import type { LabelDataTypes } from '@/types/common'
33
import { axiosInstance, formDataAxiosInstance } from '@/utils/axios'
4+
import DOMPurify from 'dompurify'
45

56
export const deleteLabelAdmin = async (id: number) => {
67
const response = await axiosInstance.delete(`/api/managements/labels/${id}`)
@@ -14,7 +15,7 @@ export const postAddLabelAdmin = async (newLabel: NewLabelTypes) => {
1415

1516
export const patchLabelAdmin = async (editLabel: LabelDataTypes) => {
1617
const response = await axiosInstance.patch(`/api/managements/labels/${editLabel.labelId}`, {
17-
labelName: editLabel.labelName,
18+
labelName: DOMPurify.sanitize(editLabel.labelName),
1819
labelColor: editLabel.labelColor
1920
})
2021
return response.data

src/api/user.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Status } from '@/types/common'
22
import type { RequestApprovePostTypes } from '@/types/manager'
33
import { axiosInstance, formDataAxiosInstance } from '@/utils/axios'
4+
import DOMPurify from 'dompurify'
45

56
export const postTaskRequest = async (formdata: FormData) => {
67
const response = await formDataAxiosInstance.post('/api/tasks', formdata)
@@ -54,7 +55,9 @@ export const getHistory = async (taskID: number | null) => {
5455
}
5556

5657
export const postComment = async (taskID: number, content: string) => {
57-
const response = await axiosInstance.post(`/api/tasks/${taskID}/comments`, { content })
58+
const response = await axiosInstance.post(`/api/tasks/${taskID}/comments`, {
59+
content: DOMPurify.sanitize(content)
60+
})
5861
return response.data
5962
}
6063

@@ -66,11 +69,6 @@ export const postCommentAttachment = async (taskID: number, formdata: FormData)
6669
return response.data
6770
}
6871

69-
export const patchComment = async (commentId: number, content: string) => {
70-
const response = await axiosInstance.patch(`/api/comments/${commentId}`, { content })
71-
return response.data
72-
}
73-
7472
export const deleteComment = async (commentId: number) => {
7573
const response = await axiosInstance.delete(`/api/comments/${commentId}`)
7674
return response.data

src/components/common/EditInformation.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ import FormButtonContainer from './FormButtonContainer.vue'
139139
import FormCheckbox from './FormCheckbox.vue'
140140
import ImageContainer from './ImageContainer.vue'
141141
import ModalView from './ModalView.vue'
142+
import DOMPurify from 'dompurify'
143+
142144
const router = useRouter()
143145
144146
const memberStore = useMemberStore()
@@ -276,7 +278,7 @@ const handleSubmit = async () => {
276278
if (isInvalid.value == false && isFull.value == false) {
277279
const formData = new FormData()
278280
const memberInfo = {
279-
name: name.value,
281+
name: DOMPurify.sanitize(name.value),
280282
isProfileImageDeleted: imageDelete.value,
281283
emailNotification: emailCheck.value,
282284
kakaoWorkNotification: kakaoWorkCheck.value

src/components/filters/FilterInput.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@
1212

1313
<script setup lang="ts">
1414
import type { Filter } from '@/types/common'
15+
import DOMPurify from 'dompurify'
1516
1617
const { title, width = '120' } = defineProps<Filter>()
1718
const emit = defineEmits(['update:value'])
1819
1920
const onValueChange = (event: Event) => {
2021
const target = event.target as HTMLInputElement
2122
setTimeout(() => {
22-
emit('update:value', target.value)
23+
emit('update:value', DOMPurify.sanitize(target.value))
2324
}, 500)
2425
}
2526
</script>

src/components/request-task/ReRequestTask.vue

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ import CategoryDropDown from './CategoryDropDown.vue'
6262
import RequestTaskFileInput from './RequestTaskFileInput.vue'
6363
import RequestTaskInput from './RequestTaskInput.vue'
6464
import RequestTaskTextArea from './RequestTaskTextArea.vue'
65+
import DOMPurify from 'dompurify'
6566
6667
const category1 = ref<Category | null>(null)
6768
const category2 = ref<SubCategory | null>(null)
@@ -151,8 +152,8 @@ const handleSubmit = async () => {
151152
152153
const taskInfo = {
153154
categoryId: category2.value.subCategoryId,
154-
title: title.value,
155-
description: description.value
155+
title: DOMPurify.sanitize(title.value),
156+
description: DOMPurify.sanitize(description.value)
156157
}
157158
158159
const taskInfoEdit = {

src/components/request-task/RequestTask.vue

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ import CategoryDropDown from './CategoryDropDown.vue'
6060
import RequestTaskFileInput from './RequestTaskFileInput.vue'
6161
import RequestTaskInput from './RequestTaskInput.vue'
6262
import RequestTaskTextArea from './RequestTaskTextArea.vue'
63+
import DOMPurify from 'dompurify'
6364
6465
const category1 = ref<Category | null>(null)
6566
const category2 = ref<SubCategory | null>(null)
@@ -136,8 +137,8 @@ const handleSubmit = async () => {
136137
const formData = new FormData()
137138
const taskInfo = {
138139
categoryId: category2.value.subCategoryId,
139-
title: title.value,
140-
description: description.value
140+
title: DOMPurify.sanitize(title.value),
141+
description: DOMPurify.sanitize(description.value)
141142
}
142143
const jsonTaskInfo = JSON.stringify(taskInfo)
143144
const newBlob = new Blob([jsonTaskInfo], { type: 'application/json' })

src/components/requested/RequestedListCard.vue

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ import { useRouter } from 'vue-router'
6565
import ModalView from '../common/ModalView.vue'
6666
import ListCardTab from '../lists/ListCardTab.vue'
6767
import TaskDetail from '../task-detail/TaskDetail.vue'
68+
import DOMPurify from 'dompurify'
6869
6970
const { info } = defineProps<{ info: RequestedListData }>()
7071
const requestedTabList: ListCardProps[] = [
@@ -107,7 +108,9 @@ const rejectRequest = async () => {
107108
modalError.value = '반려 사유를 입력해주세요'
108109
return
109110
}
110-
await axiosInstance.patch(`/api/tasks/${info.taskId}/terminate`, { reason: rejectReason.value })
111+
await axiosInstance.patch(`/api/tasks/${info.taskId}/terminate`, {
112+
reason: DOMPurify.sanitize(rejectReason.value)
113+
})
111114
toggleModal('success')
112115
}
113116

src/components/task-detail/TaskStatusList.vue

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import { statusAsColor } from '@/utils/statusAsColor'
4545
import { useQueryClient } from '@tanstack/vue-query'
4646
import { ref, watch } from 'vue'
4747
import ModalView from '../common/ModalView.vue'
48+
import DOMPurify from 'dompurify'
4849
4950
const { modelValue, isProcessor, taskId } = defineProps<TaskStatusListProps>()
5051
const modalError = ref('')
@@ -104,7 +105,9 @@ const rejectRequest = async () => {
104105
}
105106
106107
backModal.value = false
107-
await axiosInstance.patch(`/api/tasks/${taskId}/terminate`, { reason: rejectReason.value })
108+
await axiosInstance.patch(`/api/tasks/${taskId}/terminate`, {
109+
reason: DOMPurify.sanitize(rejectReason.value)
110+
})
108111
toggleModal('success')
109112
emit('update:status', 'TERMINATED')
110113
currentStatus.value = 'TERMINATED'

0 commit comments

Comments
 (0)