devsymo 5fae6c3087
fix(image): Correctly handle query parameters in image URL validation (#569)
* fix(image): Correctly handle query parameters in image URL validation

The imageUrl validator currently fails to correctly extract the file extension
when the image URL contains query parameters or fragments (e.g., 'image.qcow2?token=abc').

This change introduces a dedicated function, `getFilenameFromUrl`,
which uses the native URL object for robust parsing.
This ensures the file suffix validation is always performed on the actual filename,
ignoring any trailing parameters.

Signed-off-by: devsymo <devsymo@hotmail.com>

* fix(lint): Resolve formatting and spacing warnings

Signed-off-by: devsymo <devsymo@hotmail.com>

* fix: adjusted filename extraction logic and remove duplicate code

Signed-off-by: DevSymo <DevSymo@hotmail.com>

---------

Signed-off-by: devsymo <devsymo@hotmail.com>
Signed-off-by: DevSymo <DevSymo@hotmail.com>
2025-10-30 16:02:35 +08:00

81 lines
2.1 KiB
JavaScript

import { HCI } from '@pkg/harvester/config/labels-annotations';
export const VM_IMAGE_FILE_FORMAT = ['qcow', 'qcow2', 'raw', 'img', 'iso'];
/**
* Extracts the filename from a URL, handling query parameters and fragments
* @param {string} url - The URL to parse
* @returns {string} - The filename without query params or fragments
*/
function getFilenameFromUrl(url) {
try {
// Try to parse as a full URL
const urlObj = new URL(url);
// Get pathname and extract the last segment
const pathname = urlObj.pathname;
return pathname.split('/').filter(Boolean).pop() || '';
} catch (e) {
// If URL parsing fails, treat as a relative path
// Remove query params and fragments manually
const cleanUrl = url.split('?')[0].split('#')[0];
return cleanUrl.split('/').pop() || '';
}
}
/**
* Validates image URL format
* @param {string} url - The image URL to validate
* @param {object} getters - Vuex getters
* @param {array} errors - Array to collect validation errors
* @param {any} validatorArgs - Additional validator arguments
* @param {string} type - Type of validation ('file' or other)
* @returns {array} - Array of validation errors
*/
export function imageUrl(url, getters, errors, validatorArgs, type) {
const tipString =
type === 'file' ? 'harvester.validation.image.ruleFileTip' : 'harvester.validation.image.ruleTip';
const t = getters['i18n/t'];
if (!url || url === '') {
return errors;
}
// Extract filename, handling query parameters and fragments
const filename = getFilenameFromUrl(url);
if (!filename) {
errors.push(t(tipString));
return errors;
}
// Get file extension
const fileSuffix = filename.split('.').pop().toLowerCase();
if (!VM_IMAGE_FILE_FORMAT.includes(fileSuffix)) {
errors.push(t(tipString));
}
return errors;
}
export function fileRequired(
annotations = {},
getters,
errors,
validatorArgs,
type
) {
const t = getters['i18n/t'];
if (!annotations[HCI.IMAGE_NAME]) {
errors.push(
t('validation.required', { key: t('harvester.image.fileName') })
);
}
return errors;
}