Only convert service URL to anchor tag if it starts with http/https (#1120)

This commit is contained in:
Leendert de Borst
2025-08-15 12:02:22 +02:00
committed by Leendert de Borst
parent 437c7bb807
commit cbbfe1c611
7 changed files with 40 additions and 24 deletions

View File

@@ -21,14 +21,18 @@ const HeaderBlock: React.FC<HeaderBlockProps> = ({ credential }) => (
<div>
<h1 className="text-lg font-bold text-gray-900 dark:text-white">{credential.ServiceName}</h1>
{credential.ServiceUrl && (
<a
href={credential.ServiceUrl}
target="_blank"
rel="noopener noreferrer"
className="text-primary-600 hover:text-primary-700 dark:text-primary-400 dark:hover:text-primary-300 break-all"
>
{credential.ServiceUrl}
</a>
/^https?:\/\//i.test(credential.ServiceUrl) ? (
<a
href={credential.ServiceUrl}
target="_blank"
rel="noopener noreferrer"
className="text-primary-600 hover:text-primary-700 dark:text-primary-400 dark:hover:text-primary-300 break-all"
>
{credential.ServiceUrl}
</a>
) : (
<span className="break-all">{credential.ServiceUrl}</span>
)
)}
</div>
</div>

View File

@@ -98,7 +98,7 @@ const CredentialAddEdit: React.FC = () => {
Username: "",
Password: "",
ServiceName: "",
ServiceUrl: "",
ServiceUrl: "https://",
Notes: "",
Alias: {
FirstName: "",
@@ -401,6 +401,11 @@ const CredentialAddEdit: React.FC = () => {
birthdate = IdentityHelperUtils.normalizeBirthDateForDb(birthdate);
}
// Clean up empty protocol-only URLs
if (data.ServiceUrl === 'http://' || data.ServiceUrl === 'https://') {
data.ServiceUrl = '';
}
// If we're creating a new credential and mode is random, generate random values here
if (!isEditMode && mode === 'random') {
// Generate random values now and then read them from the form fields to manually assign to the credentialToSave object
@@ -413,6 +418,9 @@ const CredentialAddEdit: React.FC = () => {
data.Alias.BirthDate = birthdate;
data.Alias.Gender = watch('Alias.Gender');
data.Alias.Email = watch('Alias.Email');
// Clean up ServiceUrl for random mode too
const serviceUrl = watch('ServiceUrl');
data.ServiceUrl = (serviceUrl === 'http://' || serviceUrl === 'https://') ? '' : serviceUrl;
}
// Extract favicon from service URL if the credential has one

View File

@@ -237,13 +237,9 @@
"birthDate": "Birth Date",
"birthDatePlaceholder": "YYYY-MM-DD",
"metadata": "Metadata",
"errors": {
"invalidUrl": "Please enter a valid URL"
},
"validation": {
"required": "This field is required",
"serviceNameRequired": "Service name is required",
"invalidUrl": "Invalid URL format",
"invalidEmail": "Invalid email format",
"invalidDateFormat": "Date must be in YYYY-MM-DD format"
}

View File

@@ -136,11 +136,17 @@ export default function CredentialDetailsScreen() : React.ReactNode {
{credential.ServiceName}
</ThemedText>
{credential.ServiceUrl && (
<TouchableOpacity onPress={() => Linking.openURL(credential.ServiceUrl!)}>
<Text style={[styles.serviceUrl, { color: colors.primary }]}>
/^https?:\/\//i.test(credential.ServiceUrl) ? (
<TouchableOpacity onPress={() => Linking.openURL(credential.ServiceUrl!)}>
<Text style={[styles.serviceUrl, { color: colors.primary }]}>
{credential.ServiceUrl}
</Text>
</TouchableOpacity>
) : (
<Text style={styles.serviceUrl}>
{credential.ServiceUrl}
</Text>
</TouchableOpacity>
)
)}
</View>
</ThemedView>

View File

@@ -62,7 +62,7 @@ export default function AddEditCredentialScreen() : React.ReactNode {
Username: "",
Password: "",
ServiceName: "",
ServiceUrl: "",
ServiceUrl: "https://",
Notes: "",
Alias: {
FirstName: "",
@@ -255,7 +255,7 @@ export default function AddEditCredentialScreen() : React.ReactNode {
Username: data.Username,
Password: data.Password,
ServiceName: data.ServiceName,
ServiceUrl: data.ServiceUrl,
ServiceUrl: (data.ServiceUrl === 'http://' || data.ServiceUrl === 'https://') ? '' : data.ServiceUrl,
Notes: data.Notes,
Alias: {
FirstName: data.Alias.FirstName,
@@ -274,7 +274,8 @@ export default function AddEditCredentialScreen() : React.ReactNode {
credentialToSave.Username = watch('Username');
credentialToSave.Password = watch('Password');
credentialToSave.ServiceName = watch('ServiceName');
credentialToSave.ServiceUrl = watch('ServiceUrl');
const serviceUrl = watch('ServiceUrl');
credentialToSave.ServiceUrl = (serviceUrl === 'http://' || serviceUrl === 'https://') ? '' : serviceUrl;
credentialToSave.Notes = watch('Notes');
credentialToSave.Alias.FirstName = watch('Alias.FirstName');
credentialToSave.Alias.LastName = watch('Alias.LastName');

View File

@@ -360,7 +360,6 @@
"validation": {
"required": "This field is required",
"serviceNameRequired": "Service name is required",
"invalidUrlFormat": "Invalid URL format",
"invalidDateFormat": "Date must be in YYYY-MM-DD format",
"invalidEmailFormat": "Invalid email format"
},

View File

@@ -39,12 +39,14 @@ else
<h3 class="mb-1 text-xl font-bold text-gray-900 dark:text-white">@Alias.Service.Name</h3>
@if (Alias.Service.Url is not null && Alias.Service.Url.Length > 0)
{
var url = Alias.Service.Url;
if (!url.StartsWith("http://") && !url.StartsWith("https://"))
@if (Alias.Service.Url.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || Alias.Service.Url.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
{
url = "https://" + url;
<a href="@Alias.Service.Url" target="_blank" class="text-blue-500 break-all dark:text-blue-400">@Alias.Service.Url</a>
}
else
{
<span class="text-gray-700 break-all dark:text-gray-300">@Alias.Service.Url</span>
}
<a href="@url" target="_blank" class="text-blue-500 break-all dark:text-blue-400">@Alias.Service.Url</a>
}
</div>
</div>