fix: enhance website handling with URL validation

This commit is contained in:
shariquerik
2025-11-13 17:35:57 +05:30
parent c53a2675b6
commit 5c277b698e
3 changed files with 69 additions and 14 deletions

View File

@@ -163,7 +163,12 @@ import { globalStore } from '@/stores/global'
import { usersStore } from '@/stores/users'
import { statusesStore } from '@/stores/statuses'
import { getView } from '@/utils/view'
import { formatDate, timeAgo, validateIsImageFile } from '@/utils'
import {
formatDate,
timeAgo,
validateIsImageFile,
openWebsite as openExternalWebsite,
} from '@/utils'
import {
Breadcrumbs,
Avatar,
@@ -281,8 +286,12 @@ async function deleteOrganization() {
}
function openWebsite() {
if (!organization.doc.website) toast.error(__('No website found'))
else window.open(organization.doc.website, '_blank')
if (!organization.doc.website) {
toast.error(__('No website found'))
return
}
openExternalWebsite(organization.doc.website)
}
const sections = createResource({

View File

@@ -198,7 +198,13 @@ import { getMeta } from '@/stores/meta'
import { usersStore } from '@/stores/users'
import { statusesStore } from '@/stores/statuses'
import { getView } from '@/utils/view'
import { formatDate, timeAgo, validateIsImageFile, setupCustomizations } from '@/utils'
import {
formatDate,
timeAgo,
validateIsImageFile,
setupCustomizations,
openWebsite as openExternalWebsite,
} from '@/utils'
import {
Breadcrumbs,
Avatar,
@@ -235,10 +241,11 @@ const errorMessage = ref('')
const showDeleteLinkedDocModal = ref(false)
const { document: organization, permissions, scripts } = useDocument(
'CRM Organization',
props.organizationId,
)
const {
document: organization,
permissions,
scripts,
} = useDocument('CRM Organization', props.organizationId)
const canDelete = computed(() => permissions.data?.permissions?.delete || false)
@@ -318,8 +325,12 @@ function website(url) {
}
function openWebsite() {
if (!organization.doc.website) toast.error(__('No website found'))
else window.open(organization.doc.website, '_blank')
if (!organization.doc.website) {
toast.error(__('No website found'))
return
}
openExternalWebsite(organization.doc.website)
}
const sections = createResource({

View File

@@ -235,11 +235,46 @@ export function taskPriorityOptions(action, data) {
})
}
export function openWebsite(url) {
if (!url.startsWith('http://') && !url.startsWith('https://')) {
url = 'https://' + url
export function getSafeWebsiteUrl(rawUrl) {
const allowedProtocols = new Set(['http:', 'https:'])
if (!rawUrl) {
return null
}
window.open(url, '_blank')
const trimmedUrl = rawUrl.trim()
if (!trimmedUrl) {
return null
}
const urlToParse = /^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(trimmedUrl)
? trimmedUrl
: `https://${trimmedUrl}`
try {
const parsedUrl = new URL(urlToParse)
if (!allowedProtocols.has(parsedUrl.protocol)) {
return null
}
return parsedUrl.href
} catch (_error) {
return null
}
}
export function openWebsite(url) {
const safeUrl = getSafeWebsiteUrl(url)
if (!safeUrl) {
toast.error(__('Invalid website URL'))
return false
}
window.open(safeUrl, '_blank', 'noopener')
return true
}
export function website(url) {