search/app/components/BugReportInput.vue

72 lines
2.1 KiB
Vue

<script setup lang="ts">
const { $i18n } = useNuxtApp()
const t = $i18n.t
defineProps<{
modelValue: string
}>()
const emit = defineEmits<{
'update:modelValue': [value: string]
}>()
function sanitize(text: string): string {
let clean = text
clean = clean.replace(/<[^>]*>/g, '')
clean = clean.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
clean = clean.replace(/https?:\/\/[^\s]+/gi, '')
clean = clean.replace(/www\.[^\s]+/gi, '')
clean = clean.replace(/javascript\s*:/gi, '')
clean = clean.replace(/\beval\s*\(/gi, '')
clean = clean.replace(/\bon\w+\s*=/gi, '')
clean = clean.replace(/[A-Za-z0-9+/]{50,}={0,2}/g, '')
clean = clean.replace(/'\s*OR\s+\d+\s*=\s*\d+/gi, '')
clean = clean.replace(/'\s*--/g, "'")
clean = clean.replace(/'\s*;\s*DROP\s+TABLE/gi, "'")
clean = clean.replace(/UNION\s+SELECT/gi, '')
clean = clean.replace(/xp_cmdshell/gi, '')
clean = clean.replace(/EXEC\s*\(/gi, '')
clean = clean.replace(/`[^`]*`/g, '')
clean = clean.replace(/\$\(.*?\)/g, '')
clean = clean.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, '')
return clean.slice(0, 500)
}
function onInput(value: string) {
emit('update:modelValue', sanitize(value))
}
</script>
<template>
<UCard class="w-full max-w-xl shadow-sm">
<div class="flex flex-col items-center gap-4 py-4">
<UIcon name="i-lucide-bug" class="size-10 text-carpablue" />
<div class="text-center space-y-1">
<h2 class="text-lg font-semibold text-highlighted">
{{ t('feedback.title') }}
</h2>
<p class="text-sm text-muted leading-relaxed max-w-sm">
{{ t('feedback.description') }}
</p>
</div>
<UTextarea
:model-value="modelValue"
:placeholder="t('feedback.placeholder')"
:rows="6"
:maxlength="500"
autoresize
size="lg"
class="w-full"
@update:model-value="onInput($event)"
/>
<div class="flex items-center justify-between w-full text-xs text-muted">
<span>{{ t('feedback.anonymous_notice') }}</span>
<span>{{ modelValue.length }}/500</span>
</div>
</div>
</UCard>
</template>