Repository: research-software-directory/RSD-as-a-service
Pull request #1447: feat: don't allow white spaces in URLs
Author: login: paulastock
Created: 2025-04-01T14:23:12Z | Merged: 2025-04-15T08:11:37Z

Fixes #1442 

Changes proposed in this pull request:

* update regex in database tables and patterns for frontend input fields to not allow white spaces within or at the end of URLs
* allow URLs without dots in frontend URL fields for testing purposes (e.g. `http:///localhost`)

How to test:

* `docker compose down --volumes && docker compose build --parallel && docker compose up --scale data-generation=1`
*  Sign in as admin
*  Edit any URL e.g. repository URL
*  When entering a white space within or at the end of the otherwise valid URL, an error message should appear

Cleanup for production:
When this becomes part of production, existing entries in tables `software`, `repository_url`, `package_manager`, `url_for_project`, `mention` need cleanup first.

PR Checklist:

*   [ ] Increase version numbers in `docker-compose.yml`
*   [x] Link to a GitHub issue
*   [ ] Update documentation
*   [ ] Tests


Linked issue #1442: "Not a valid GitHub url" in case of trailing spaces
We have had the case where software entries were not scraped and were shown as "we cannot read the commit history", because the maintainers added a url with a trailing space at the end.

To mitigate this I'd suggest to automatically strip all whitepsaces after pasting the url into the Repository URL field. This could be reused for many other use cases, e.g. Get started URL, Software DOI, ..
Linked issue comments: user: login: ewan-escience
created_at: 2025-03-31T13:12:32Z
body: For URLs, we probably shouldn't allow any white space at all. We already enforce the regex `^https?://`. We could expand that to `^https?://\S+$`. We could also use a very specialised regex for URLs, but we need to make sure it covers all cases and it should ideally have an official source.

For other fields that should not have redundant spaces, have a look at #1433.
author_association: COLLABORATOR

Diff:
diff --git a/database/004-create-software-table.sql b/database/004-create-software-table.sql
index 61fa132f1..203b63e7e 100644
--- a/database/004-create-software-table.sql
+++ b/database/004-create-software-table.sql
@@ -1,11 +1,12 @@
 -- SPDX-FileCopyrightText: 2021 - 2024 Ewan Cahen (Netherlands eScience Center) <e.cahen@esciencecenter.nl>
 -- SPDX-FileCopyrightText: 2021 - 2024 Netherlands eScience Center
+-- SPDX-FileCopyrightText: 2022 - 2025 Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences
 -- SPDX-FileCopyrightText: 2022 Christian Meeßen (GFZ) <christian.meessen@gfz-potsdam.de>
 -- SPDX-FileCopyrightText: 2022 Dusan Mijatovic (dv4all)
--- SPDX-FileCopyrightText: 2022 Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences
 -- SPDX-FileCopyrightText: 2022 dv4all
 -- SPDX-FileCopyrightText: 2023 Diego Alonso Alvarez (ICL) <d.alonso-alvarez@imperial.ac.uk>
 -- SPDX-FileCopyrightText: 2023 Imperial College London
+-- SPDX-FileCopyrightText: 2025 Paula Stock (GFZ) <paula.stock@gfz.de>
 --
 -- SPDX-License-Identifier: Apache-2.0
 
@@ -26,9 +27,9 @@ CREATE TABLE software (
 	closed_source BOOLEAN DEFAULT FALSE NOT NULL,
 	concept_doi CITEXT CHECK (concept_doi ~ '^10(\.\w+)+/\S+$' AND LENGTH(concept_doi) <= 255),
 	description VARCHAR(10000),
-	description_url VARCHAR(200) CHECK (description_url ~ '^https?://'),
+	description_url VARCHAR(200) CHECK (description_url ~ '^https?://\S+$'),
 	description_type description_type DEFAULT 'markdown' NOT NULL,
-	get_started_url VARCHAR(200) CHECK (get_started_url ~ '^https?://'),
+	get_started_url VARCHAR(200) CHECK (get_started_url ~ '^https?://\S+$'),
 	image_id VARCHAR(40) REFERENCES image(id),
 	is_published BOOLEAN DEFAULT FALSE NOT NULL,
 	short_statement VARCHAR(300),
diff --git a/database/005-create-relations-for-software.sql b/database/005-create-relations-for-software.sql
index aa76dcce6..88ff229eb 100644
--- a/database/005-create-relations-for-software.sql
+++ b/database/005-create-relations-for-software.sql
@@ -2,10 +2,11 @@
 -- SPDX-FileCopyrightText: 2021 - 2025 Netherlands eScience Center
 -- SPDX-FileCopyrightText: 2022 - 2023 Dusan Mijatovic (dv4all)
 -- SPDX-FileCopyrightText: 2022 - 2024 dv4all
+-- SPDX-FileCopyrightText: 2022 - 2025 Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences
 -- SPDX-FileCopyrightText: 2022 Christian Meeßen (GFZ) <christian.meessen@gfz-potsdam.de>
--- SPDX-FileCopyrightText: 2022 Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences
 -- SPDX-FileCopyrightText: 2023 - 2024 Dusan Mijatovic (Netherlands eScience Center)
 -- SPDX-FileCopyrightText: 2024 Dusan Mijatovic (dv4all) (dv4all)
+-- SPDX-FileCopyrightText: 2025 Paula Stock (GFZ) <paula.stock@gfz.de>
 --
 -- SPDX-License-Identifier: Apache-2.0
 
@@ -18,7 +19,7 @@ CREATE TYPE platform_type AS ENUM (
 
 CREATE TABLE repository_url (
 	software UUID REFERENCES software (id) PRIMARY KEY,
-	url VARCHAR(200) NOT NULL CHECK (url ~ '^https?://'),
+	url VARCHAR(200) NOT NULL CHECK (url ~ '^https?://\S+$'),
 	code_platform platform_type NOT NULL DEFAULT 'other',
 	archived BOOLEAN,
 	license VARCHAR(200),
@@ -64,7 +65,7 @@ CREATE TYPE package_manager_type AS ENUM (
 CREATE TABLE package_manager (
 	id UUID PRIMARY KEY,
 	software UUID REFERENCES software (id) NOT NULL,
-	url VARCHAR(200) NOT NULL CHECK (url ~ '^https?://'),
+	url VARCHAR(200) NOT NULL CHECK (url ~ '^https?://\S+$'),
 	package_manager package_manager_type NOT NULL DEFAULT 'other',
 	download_count BIGINT,
 	download_count_last_error VARCHAR(500),
@@ -113,7 +114,7 @@ CREATE TABLE license_for_software (
 	software UUID REFERENCES software (id) NOT NULL,
 	license VARCHAR(100) NOT NULL,
 	name VARCHAR(200) NULL,
-	reference VARCHAR(200) NULL CHECK (reference ~ '^https?://'),
+	reference VARCHAR(200) NULL CHECK (reference ~ '^https?://\S+$'),
 	open_source BOOLEAN NOT NULL DEFAULT TRUE,
 	UNIQUE(software, license),
 	created_at TIMESTAMPTZ NOT NULL,
diff --git a/database/006-create-project-table.sql b/database/006-create-project-table.sql
index da8acc6be..1fb430ca6 100644
--- a/database/006-create-project-table.sql
+++ b/database/006-create-project-table.sql
@@ -2,6 +2,8 @@
 -- SPDX-FileCopyrightText: 2022 - 2024 Netherlands eScience Center
 -- SPDX-FileCopyrightText: 2022 Dusan Mijatovic (dv4all)
 -- SPDX-FileCopyrightText: 2022 dv4all
+-- SPDX-FileCopyrightText: 2025 Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences
+-- SPDX-FileCopyrightText: 2025 Paula Stock (GFZ) <paula.stock@gfz.de>
 --
 -- SPDX-License-Identifier: Apache-2.0
 
@@ -57,7 +59,7 @@ CREATE TABLE url_for_project (
 	id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
 	project UUID REFERENCES project (id),
 	title VARCHAR(100) NOT NULL,
-	url VARCHAR(200) NOT NULL CHECK (url ~ '^https?://'),
+	url VARCHAR(200) NOT NULL CHECK (url ~ '^https?://\S+$'),
 	position INTEGER
 );
 
diff --git a/database/011-create-mention-table.sql b/database/011-create-mention-table.sql
index 146effeef..07d678d63 100644
--- a/database/011-create-mention-table.sql
+++ b/database/011-create-mention-table.sql
@@ -2,6 +2,8 @@
 -- SPDX-FileCopyrightText: 2021 - 2024 Netherlands eScience Center
 -- SPDX-FileCopyrightText: 2022 Dusan Mijatovic (dv4all)
 -- SPDX-FileCopyrightText: 2022 dv4all
+-- SPDX-FileCopyrightText: 2025 Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences
+-- SPDX-FileCopyrightText: 2025 Paula Stock (GFZ) <paula.stock@gfz.de>
 --
 -- SPDX-License-Identifier: Apache-2.0
 
@@ -32,14 +34,14 @@ CREATE TABLE mention (
 	doi CITEXT UNIQUE CHECK (doi ~ '^10(\.\w+)+/\S+$' AND LENGTH(doi) <= 255),
 	doi_registration_date TIMESTAMPTZ,
 	openalex_id CITEXT UNIQUE CHECK (openalex_id ~ '^https://openalex\.org/[WwAaSsIiCcPpFf]\d{3,13}$'),
-	url VARCHAR(500) CHECK (url ~ '^https?://'),
+	url VARCHAR(500) CHECK (url ~ '^https?://\S+$'),
 	title VARCHAR(3000) NOT NULL,
 	authors VARCHAR(50000),
 	publisher VARCHAR(255),
 	publication_year SMALLINT,
 	journal VARCHAR(500),
 	page VARCHAR(50),
-	image_url VARCHAR(500) CHECK (image_url ~ '^https?://'),
+	image_url VARCHAR(500) CHECK (image_url ~ '^https?://\S+$'),
 	mention_type mention_type NOT NULL,
 	source VARCHAR(50) NOT NULL,
 	version VARCHAR(100),
diff --git a/frontend/components/mention/config.ts b/frontend/components/mention/config.ts
index 89880b692..75797fe16 100644
--- a/frontend/components/mention/config.ts
+++ b/frontend/components/mention/config.ts
@@ -2,6 +2,8 @@
 // SPDX-FileCopyrightText: 2022 - 2023 dv4all
 // SPDX-FileCopyrightText: 2022 - 2024 Ewan Cahen (Netherlands eScience Center) <e.cahen@esciencecenter.nl>
 // SPDX-FileCopyrightText: 2022 - 2024 Netherlands eScience Center
+// SPDX-FileCopyrightText: 2025 Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences
+// SPDX-FileCopyrightText: 2025 Paula Stock (GFZ) <paula.stock@gfz.de>
 //
 // SPDX-License-Identifier: Apache-2.0
 
@@ -119,8 +121,8 @@ export const mentionModal = {
         message: 'Maximum length is 500'
       },
       pattern: {
-        value: /^https?:\/\/.+\..+/,
-        message: 'Url should start with http(s):// have at least one dot (.)'
+        value: /^https?:\/\/\S+$/,
+        message: 'Url must start with http(s):// and cannot contain white spaces'
       }
     }
   },
@@ -156,8 +158,8 @@ export const mentionModal = {
         message: 'Maximum length is 500'
       },
       pattern: {
-        value: /^https?:\/\/.+\..+/,
-        message: 'Url should start with http(s):// have at least one dot (.)'
+        value: /^https?:\/\/\S+$/,
+        message: 'Url must start with http(s):// and cannot contain white spaces'
       }
     }
   },
diff --git a/frontend/components/organisation/settings/general/generalSettingsConfig.ts b/frontend/components/organisation/settings/general/generalSettingsConfig.ts
index a7df60bfc..c5d4a929c 100644
--- a/frontend/components/organisation/settings/general/generalSettingsConfig.ts
+++ b/frontend/components/organisation/settings/general/generalSettingsConfig.ts
@@ -2,8 +2,9 @@
 // SPDX-FileCopyrightText: 2022 dv4all
 // SPDX-FileCopyrightText: 2023 Dusan Mijatovic (Netherlands eScience Center)
 // SPDX-FileCopyrightText: 2023 Netherlands eScience Center
+// SPDX-FileCopyrightText: 2024 - 2025 Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences
 // SPDX-FileCopyrightText: 2024 Christian Meeßen (GFZ) <christian.meessen@gfz-potsdam.de>
-// SPDX-FileCopyrightText: 2024 Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences
+// SPDX-FileCopyrightText: 2025 Paula Stock (GFZ) <paula.stock@gfz.de>
 //
 // SPDX-License-Identifier: Apache-2.0
 
@@ -32,8 +33,8 @@ export const generalSettingsConfig = {
       minLength: {value: 6, message: 'Minimum length is 6'},
       maxLength: {value: 200, message: 'Maximum length is 200'},
       pattern: {
-        value: /^https?:\/\/.+\..+/,
-        message: 'Url should start with http(s):// and have at least one dot (.)'
+        value: /^https?:\/\/\S+$/,
+        message: 'Url should start with http(s):// and cannot contain white spaces'
       }
     }
   },
@@ -65,8 +66,8 @@ export const generalSettingsConfig = {
       minLength: {value: 16, message: 'Minimum length is 16'},
       maxLength: {value: 200, message: 'Maximum length is 200'},
       pattern: {
-        value: /^https:\/\/ror.org\/.+/,
-        message: 'ROR id is url and starts with https://ror.org/'
+        value: /^https:\/\/ror.org\/\S+$/,
+        message: 'ROR ID must start with https://ror.org/ and cannot include white spaces'
       }
     }
   },
diff --git a/frontend/components/projects/edit/information/config.ts b/frontend/components/projects/edit/information/config.ts
index 9a9e42c80..2848a8bc5 100644
--- a/frontend/components/projects/edit/information/config.ts
+++ b/frontend/components/projects/edit/information/config.ts
@@ -1,11 +1,12 @@
 // SPDX-FileCopyrightText: 2022 - 2024 Netherlands eScience Center
 // SPDX-FileCopyrightText: 2022 - 2024 dv4all
+// SPDX-FileCopyrightText: 2022 - 2025 Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences
 // SPDX-FileCopyrightText: 2022 Christian Meeßen (GFZ) <christian.meessen@gfz-potsdam.de>
 // SPDX-FileCopyrightText: 2022 Dusan Mijatovic (dv4all)
 // SPDX-FileCopyrightText: 2022 Ewan Cahen (Netherlands eScience Center) <e.cahen@esciencecenter.nl>
-// SPDX-FileCopyrightText: 2022 Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences
 // SPDX-FileCopyrightText: 2024 Dusan Mijatovic (Netherlands eScience Center)
 // SPDX-FileCopyrightText: 2024 Dusan Mijatovic (dv4all) (dv4all)
+// SPDX-FileCopyrightText: 2025 Paula Stock (GFZ) <paula.stock@gfz.de>
 //
 // SPDX-License-Identifier: Apache-2.0
 
@@ -113,8 +114,8 @@ export const projectInformation = {
       validation: {
         required: 'Link url is required',
         pattern: {
-          value: /^https?:\/\/.+\..+/,
-          message: 'Url should start with http(s):// and have at least one dot (.)'
+          value: /^https?:\/\/\S+$/,
+          message: 'Url should start with http(s):// and cannot include white spaces'
         }
       }
     }
diff --git a/frontend/components/projects/edit/organisations/config.ts b/frontend/components/projects/edit/organisations/config.ts
index 6eedbe388..c1033749a 100644
--- a/frontend/components/projects/edit/organisations/config.ts
+++ b/frontend/components/projects/edit/organisations/config.ts
@@ -2,6 +2,8 @@
 // SPDX-FileCopyrightText: 2022 dv4all
 // SPDX-FileCopyrightText: 2024 Ewan Cahen (Netherlands eScience Center) <e.cahen@esciencecenter.nl>
 // SPDX-FileCopyrightText: 2024 Netherlands eScience Center
+// SPDX-FileCopyrightText: 2025 Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences
+// SPDX-FileCopyrightText: 2025 Paula Stock (GFZ) <paula.stock@gfz.de>
 //
 // SPDX-License-Identifier: Apache-2.0
 
@@ -34,8 +36,8 @@ export const cfgOrganisations = {
       minLength: {value: 6, message: 'Minimum length is 6'},
       maxLength: {value: 200, message: 'Maximum length is 200'},
       pattern: {
-        value: /^https?:\/\/.+\..+/,
-        message: 'Url should start with http(s):// and have at least one dot (.)'
+        value: /^https?:\/\/\S+$/,
+        message: 'Url must start with http(s):// and cannot include white spaces'
       }
     }
   },
diff --git a/frontend/components/software/edit/editSoftwareConfig.tsx b/frontend/components/software/edit/editSoftwareConfig.tsx
index 58b916f4f..018467bbe 100644
--- a/frontend/components/software/edit/editSoftwareConfig.tsx
+++ b/frontend/components/software/edit/editSoftwareConfig.tsx
@@ -1,13 +1,14 @@
 // SPDX-FileCopyrightText: 2022 - 2023 Dusan Mijatovic (dv4all)
-// SPDX-FileCopyrightText: 2022 - 2023 Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences
 // SPDX-FileCopyrightText: 2022 - 2024 Ewan Cahen (Netherlands eScience Center) <e.cahen@esciencecenter.nl>
 // SPDX-FileCopyrightText: 2022 - 2024 dv4all
+// SPDX-FileCopyrightText: 2022 - 2025 Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences
 // SPDX-FileCopyrightText: 2022 - 2025 Netherlands eScience Center
 // SPDX-FileCopyrightText: 2022 Christian Meeßen (GFZ) <christian.meessen@gfz-potsdam.de>
 // SPDX-FileCopyrightText: 2022 Matthias Rüster (GFZ) <matthias.ruester@gfz-potsdam.de>
 // SPDX-FileCopyrightText: 2023 - 2025 Dusan Mijatovic (Netherlands eScience Center)
 // SPDX-FileCopyrightText: 2023 Felix Mühlbauer (GFZ) <felix.muehlbauer@gfz-potsdam.de>
 // SPDX-FileCopyrightText: 2024 Dusan Mijatovic (dv4all) (dv4all)
+// SPDX-FileCopyrightText: 2025 Paula Stock (GFZ) <paula.stock@gfz.de>
 //
 // SPDX-License-Identifier: Apache-2.0
 
@@ -141,8 +142,8 @@ export const organisationInformation = {
       minLength: {value: 6, message: 'Minimum length is 6'},
       maxLength: {value: 200, message: 'Maximum length is 200'},
       pattern: {
-        value: /^https?:\/\/.+\..+/,
-        message: 'URL should start with http(s):// and have at least one dot (.)'
+        value: /^https?:\/\/\S+$/,
+        message: 'URL should start with http(s):// and cannot include white spaces'
       }
     }
   },
@@ -217,8 +218,8 @@ export const mentionInformation = {
     help: 'Provide URL to publication',
     validation: {
       pattern: {
-        value: /^https?:\/\/.+\..+/,
-        message: 'URL should start with http(s):// have at least one dot (.)'
+        value: /^https?:\/\/\S+$/,
+        message: 'URL should start with http(s):// and cannot include white spaces'
       }
     }
   },
@@ -233,8 +234,8 @@ export const mentionInformation = {
     help: 'Provide URL to image',
     validation: {
       pattern: {
-        value: /^https?:\/\/.+\..+/,
-        message: 'URL should start with http(s):// have at least one dot (.)'
+        value: /^https?:\/\/\S+$/,
+        message: 'URL must start with http(s):// and cannot include white spaces'
       }
     }
   },
diff --git a/frontend/components/software/edit/links/config.tsx b/frontend/components/software/edit/links/config.tsx
index 76634d572..83fa9f64a 100644
--- a/frontend/components/software/edit/links/config.tsx
+++ b/frontend/components/software/edit/links/config.tsx
@@ -1,7 +1,8 @@
+// SPDX-FileCopyrightText: 2024 - 2025 Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences
 // SPDX-FileCopyrightText: 2024 Dusan Mijatovic (Netherlands eScience Center)
 // SPDX-FileCopyrightText: 2024 Felix Mühlbauer (GFZ) <felix.muehlbauer@gfz-potsdam.de>
-// SPDX-FileCopyrightText: 2024 Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences
 // SPDX-FileCopyrightText: 2024 Netherlands eScience Center
+// SPDX-FileCopyrightText: 2025 Paula Stock (GFZ) <paula.stock@gfz.de>
 //
 // SPDX-License-Identifier: Apache-2.0
 
@@ -14,8 +15,8 @@ export const config={
     validation: {
       maxLength: {value: 200, message: 'Maximum length is 200'},
       pattern: {
-        value: /^https?:\/\/.+\..+/,
-        message: 'URL should start with http(s):// and use at least one dot (.)'
+        value: /^https?:\/\/\S+$/,
+        message: 'URL must start with http(s):// and cannot include white spaces'
       }
     }
   },
@@ -25,8 +26,8 @@ export const config={
     validation: {
       maxLength: {value: 200, message: 'Maximum length is 200'},
       pattern: {
-        value: /^https?:\/\/.+\..+/,
-        message: 'URL should start with http(s)://, have at least one dot (.) and at least one slash (/).'
+        value: /^https?:\/\/\S+$/,
+        message: 'URL must start with http(s):// and cannot include white spaces.'
       }
     }
   },
@@ -124,8 +125,8 @@ export const config={
           minLength: {value: 10, message: 'Minimum length is 10'},
           maxLength: {value: 200, message: 'Maximum length is 200'},
           pattern: {
-            value: /^https?:\/\/.+\..+/,
-            message: 'URL should start with http(s):// and use at least one dot (.)'
+            value: /^https?:\/\/\S+$/,
+            message: 'URL must start with http(s):// and cannot include white spaces'
           }
         }
       },
diff --git a/frontend/components/software/edit/package-managers/config.ts b/frontend/components/software/edit/package-managers/config.ts
index d2e72b998..1e4b37c0c 100644
--- a/frontend/components/software/edit/package-managers/config.ts
+++ b/frontend/components/software/edit/package-managers/config.ts
@@ -3,6 +3,8 @@
 // SPDX-FileCopyrightText: 2023 Dusan Mijatovic (dv4all) (dv4all)
 // SPDX-FileCopyrightText: 2024 Dusan Mijatovic (Netherlands eScience Center)
 // SPDX-FileCopyrightText: 2024 Netherlands eScience Center
+// SPDX-FileCopyrightText: 2025 Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences
+// SPDX-FileCopyrightText: 2025 Paula Stock (GFZ) <paula.stock@gfz.de>
 //
 // SPDX-License-Identifier: Apache-2.0
 
@@ -19,8 +21,8 @@ export const config = {
           message: 'Maximum length is 200'
         },
         pattern: {
-          value: /^https?:\/\/.+\..+/,
-          message: 'URL should start with http(s):// and use at least one dot (.)'
+          value: /^https?:\/\/\S+$/,
+          message: 'URL must start with http(s):// and cannot include white spaces'
         }
       }
     },


Reviews:
user: login: cmeessen
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T09:32:14Z
user: login: cmeessen
body: Nice job, just some minor textual changes.
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T09:39:17Z
user: login: ewan-escience
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-07T09:56:24Z
user: login: ewan-escience
body: Nice work! I agree with @cmeessen's review and a made a suggestion earlier. Another suggestion I have is to remove the requirement to have a dot in the URL field (so make the regex `/^https?:\/\/\S+$/`), because e.g. `http:///localhost` doesn't have a dot, which can be used in testing. This should also get rid of the security warnings.
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-04-07T09:59:33Z
user: login: ewan-escience
body: One small change requested :)
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-04-11T08:43:56Z
user: login: dmijatovic
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-11T12:34:56Z
user: login: ewan-escience
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-11T12:41:23Z
user: login: ewan-escience
body: Nice work! Here is some (untested) migration code:

```sql
-- update columns to remove white space from URLs
UPDATE software SET description_url = regexp_replace(description_url, '\s+', '', 'g') WHERE description_url !~ '^https?://\S+$';
UPDATE software SET get_started_url = regexp_replace(get_started_url, '\s+', '', 'g') WHERE get_started_url !~ '^https?://\S+$';
UPDATE repository_url SET url = regexp_replace(url, '\s+', '', 'g') WHERE url !~ '^https?://\S+$';
UPDATE package_manager SET url = regexp_replace(url, '\s+', '', 'g') WHERE url !~ '^https?://\S+$';
UPDATE license_for_software SET reference = regexp_replace(reference, '\s+', '', 'g') WHERE reference !~ '^https?://\S+$';
UPDATE url_for_project SET url = regexp_replace(url, '\s+', '', 'g') WHERE url !~ '^https?://\S+$';
UPDATE mention SET url = regexp_replace(url, '\s+', '', 'g') WHERE url !~ '^https?://\S+$';
UPDATE mention SET image_url = regexp_replace(image_url, '\s+', '', 'g') WHERE image_url !~ '^https?://\S+$';

-- select columns whose URLs are still not compliant, manual cleaning needed
SELECT description_url FROM software WHERE description_url !~ '^https?://\S+$';
SELECT get_started_url FROM software WHERE get_started_url !~ '^https?://\S+$';
SELECT url FROM repository_url WHERE url !~ '^https?://\S+$';
SELECT url FROM package_manager WHERE url !~ '^https?://\S+$';
SELECT reference FROM license_for_software WHERE reference !~ '^https?://\S+$';
SELECT url FROM url_for_project WHERE url !~ '^https?://\S+$';
SELECT url FROM mention WHERE url !~ '^https?://\S+$';
SELECT image_url FROM mention WHERE image_url !~ '^https?://\S+$';
```
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-14T08:06:44Z
user: login: cmeessen
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-15T05:37:09Z

Review comments:
diff_hunk: @@ -119,8 +121,8 @@ export const mentionModal = {
         message: 'Maximum length is 500'
       },
       pattern: {
-        value: /^https?:\/\/.+\..+/,
-        message: 'Url should start with http(s):// have at least one dot (.)'
+        value: /^https?:\/\/\S+\.\S+$/,
+        message: 'Url should start with http(s):// have at least one dot (.) and cannot contain white spaces'
path: frontend/components/mention/config.ts
user: login: cmeessen
body: ```suggestion
        message: 'Url should start with http(s)://, have at least one dot (.) and cannot contain white spaces'
```
created_at: 2025-04-07T09:32:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -156,8 +158,8 @@ export const mentionModal = {
         message: 'Maximum length is 500'
       },
       pattern: {
-        value: /^https?:\/\/.+\..+/,
-        message: 'Url should start with http(s):// have at least one dot (.)'
+        value: /^https?:\/\/\S+\.\S+$/,
+        message: 'Url should start with http(s):// have at least one dot (.) and cannot contain white spaces'
path: frontend/components/mention/config.ts
user: login: cmeessen
body: ```suggestion
        message: 'Url should start with http(s)://, have at least one dot (.) and cannot contain white spaces'
```
created_at: 2025-04-07T09:32:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -32,8 +33,8 @@ export const generalSettingsConfig = {
       minLength: {value: 6, message: 'Minimum length is 6'},
       maxLength: {value: 200, message: 'Maximum length is 200'},
       pattern: {
-        value: /^https?:\/\/.+\..+/,
-        message: 'Url should start with http(s):// and have at least one dot (.)'
+        value: /^https?:\/\/\S+\.\S+$/,
+        message: 'Url should start with http(s):// and have at least one dot (.) and cannot contain white spaces'
path: frontend/components/organisation/settings/general/generalSettingsConfig.ts
user: login: cmeessen
body: ```suggestion
        message: 'Url should start with http(s)://, have at least one dot (.) and cannot contain white spaces'
```
created_at: 2025-04-07T09:32:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -32,8 +33,8 @@ export const generalSettingsConfig = {
       minLength: {value: 6, message: 'Minimum length is 6'},
       maxLength: {value: 200, message: 'Maximum length is 200'},
       pattern: {
-        value: /^https?:\/\/.+\..+/,
-        message: 'Url should start with http(s):// and have at least one dot (.)'
+        value: /^https?:\/\/\S+\.\S+$/,
+        message: 'Url should start with http(s):// and have at least one dot (.) and cannot contain white spaces'
path: frontend/components/organisation/settings/general/generalSettingsConfig.ts
user: login: cmeessen
body: ```suggestion
        message: 'Url should start with http(s)://, have at least one dot (.) and cannot contain white spaces'
```
created_at: 2025-04-07T09:32:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -65,8 +66,8 @@ export const generalSettingsConfig = {
       minLength: {value: 16, message: 'Minimum length is 16'},
       maxLength: {value: 200, message: 'Maximum length is 200'},
       pattern: {
-        value: /^https:\/\/ror.org\/.+/,
-        message: 'ROR id is url and starts with https://ror.org/'
+        value: /^https:\/\/ror.org\/\S+$/,
+        message: 'ROR id is url and starts with https://ror.org/ and cannot include white spaces'
path: frontend/components/organisation/settings/general/generalSettingsConfig.ts
user: login: cmeessen
body: ```suggestion
        message: 'ROR ID must start with https://ror.org/ and cannot include white spaces'
```
created_at: 2025-04-07T09:35:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -113,8 +114,8 @@ export const projectInformation = {
       validation: {
         required: 'Link url is required',
         pattern: {
-          value: /^https?:\/\/.+\..+/,
-          message: 'Url should start with http(s):// and have at least one dot (.)'
+          value: /^https?:\/\/\S+\.\S+$/,
+          message: 'Url should start with http(s):// and have at least one dot (.) and cannot include white spaces'
path: frontend/components/projects/edit/information/config.ts
user: login: cmeessen
body: ```suggestion
          message: 'Url must start with http(s)://, have at least one dot (.) and cannot include white spaces'
```

Why do we allow http here?
created_at: 2025-04-07T09:35:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -34,8 +36,8 @@ export const cfgOrganisations = {
       minLength: {value: 6, message: 'Minimum length is 6'},
       maxLength: {value: 200, message: 'Maximum length is 200'},
       pattern: {
-        value: /^https?:\/\/.+\..+/,
-        message: 'Url should start with http(s):// and have at least one dot (.)'
+        value: /^https?:\/\/\S+\.\S+$/,
+        message: 'Url should start with http(s):// and have at least one dot (.) and cannot include white spaces'
path: frontend/components/projects/edit/organisations/config.ts
user: login: cmeessen
body: ```suggestion
        message: 'Url must start with http(s)://, have at least one dot (.) and cannot include white spaces'
```
created_at: 2025-04-07T09:36:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -141,8 +142,8 @@ export const organisationInformation = {
       minLength: {value: 6, message: 'Minimum length is 6'},
       maxLength: {value: 200, message: 'Maximum length is 200'},
       pattern: {
-        value: /^https?:\/\/.+\..+/,
-        message: 'URL should start with http(s):// and have at least one dot (.)'
+        value: /^https?:\/\/\S+\.\S+$/,
+        message: 'URL should start with http(s):// and have at least one dot (.) and cannot include white spaces'
path: frontend/components/software/edit/editSoftwareConfig.tsx
user: login: cmeessen
body: ```suggestion
        message: 'URL must start with http(s)://, have at least one dot (.) and cannot include white spaces'
```
created_at: 2025-04-07T09:37:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -217,8 +218,8 @@ export const mentionInformation = {
     help: 'Provide URL to publication',
     validation: {
       pattern: {
-        value: /^https?:\/\/.+\..+/,
-        message: 'URL should start with http(s):// have at least one dot (.)'
+        value: /^https?:\/\/\S+\.\S+$/,
+        message: 'URL should start with http(s):// have at least one dot (.) and cannot include white spaces'
path: frontend/components/software/edit/editSoftwareConfig.tsx
user: login: cmeessen
body: ```suggestion
        message: 'URL must start with http(s)://, have at least one dot (.) and cannot include white spaces'
```
created_at: 2025-04-07T09:37:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -233,8 +234,8 @@ export const mentionInformation = {
     help: 'Provide URL to image',
     validation: {
       pattern: {
-        value: /^https?:\/\/.+\..+/,
-        message: 'URL should start with http(s):// have at least one dot (.)'
+        value: /^https?:\/\/\S+\.\S+$/,
+        message: 'URL should start with http(s):// have at least one dot (.) and cannot include white spaces'
path: frontend/components/software/edit/editSoftwareConfig.tsx
user: login: cmeessen
body: ```suggestion
        message: 'URL must start with http(s)://, have at least one dot (.) and cannot include white spaces'
```
created_at: 2025-04-07T09:37:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -14,8 +15,8 @@ export const config={
     validation: {
       maxLength: {value: 200, message: 'Maximum length is 200'},
       pattern: {
-        value: /^https?:\/\/.+\..+/,
-        message: 'URL should start with http(s):// and use at least one dot (.)'
+        value: /^https?:\/\/\S+\.\S+$/,
+        message: 'URL should start with http(s):// and use at least one dot (.) and cannot include white spaces'
path: frontend/components/software/edit/links/config.tsx
user: login: cmeessen
body: ```suggestion
        message: 'URL must start with http(s)://, use at least one dot (.) and cannot include white spaces'
```
created_at: 2025-04-07T09:38:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -25,8 +26,8 @@ export const config={
     validation: {
       maxLength: {value: 200, message: 'Maximum length is 200'},
       pattern: {
-        value: /^https?:\/\/.+\..+/,
-        message: 'URL should start with http(s)://, have at least one dot (.) and at least one slash (/).'
+        value: /^https?:\/\/\S+\.\S+$/,
+        message: 'URL should start with http(s)://, have at least one dot (.) and at least one slash (/) and cannot include white spaces.'
path: frontend/components/software/edit/links/config.tsx
user: login: cmeessen
body: ```suggestion
        message: 'URL must start with http(s)://, have at least one dot (.), at least one slash (/) and cannot include white spaces'
```
created_at: 2025-04-07T09:38:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -124,8 +125,8 @@ export const config={
           minLength: {value: 10, message: 'Minimum length is 10'},
           maxLength: {value: 200, message: 'Maximum length is 200'},
           pattern: {
-            value: /^https?:\/\/.+\..+/,
-            message: 'URL should start with http(s):// and use at least one dot (.)'
+            value: /^https?:\/\/\S+\.\S+$/,
+            message: 'URL should start with http(s):// and use at least one dot (.) and cannot include white spaces'
path: frontend/components/software/edit/links/config.tsx
user: login: cmeessen
body: ```suggestion
            message: 'URL must start with http(s)://, use at least one dot (.) and cannot include white spaces'
```
created_at: 2025-04-07T09:38:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -19,8 +21,8 @@ export const config = {
           message: 'Maximum length is 200'
         },
         pattern: {
-          value: /^https?:\/\/.+\..+/,
-          message: 'URL should start with http(s):// and use at least one dot (.)'
+          value: /^https?:\/\/\S+\.\S+$/,
+          message: 'URL should start with http(s):// and use at least one dot (.) and cannot include white spaces'
path: frontend/components/software/edit/package-managers/config.ts
user: login: cmeessen
body: ```suggestion
          message: 'URL must start with http(s)://, use at least one dot (.) and cannot include white spaces'
```
created_at: 2025-04-07T09:38:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -26,9 +27,9 @@ CREATE TABLE software (
 	closed_source BOOLEAN DEFAULT FALSE NOT NULL,
 	concept_doi CITEXT CHECK (concept_doi ~ '^10(\.\w+)+/\S+$' AND LENGTH(concept_doi) <= 255),
 	description VARCHAR(10000),
-	description_url VARCHAR(200) CHECK (description_url ~ '^https?://'),
+	description_url VARCHAR(200) CHECK (description_url ~ '^https?://\S+'),
path: database/004-create-software-table.sql
user: login: ewan-escience
body: Please add a trailing dollar to the regex (my bad, I forgot to put this in the issue when suggesting this regex), so `^https?://\S+$`. The trailing dollar prevents trailing white space.
created_at: 2025-04-07T09:56:23Z
author_association: COLLABORATOR
diff_hunk: @@ -26,9 +27,9 @@ CREATE TABLE software (
 	closed_source BOOLEAN DEFAULT FALSE NOT NULL,
 	concept_doi CITEXT CHECK (concept_doi ~ '^10(\.\w+)+/\S+$' AND LENGTH(concept_doi) <= 255),
 	description VARCHAR(10000),
-	description_url VARCHAR(200) CHECK (description_url ~ '^https?://'),
+	description_url VARCHAR(200) CHECK (description_url ~ '^https?://\S+'),
 	description_type description_type DEFAULT 'markdown' NOT NULL,
-	get_started_url VARCHAR(200) CHECK (get_started_url ~ '^https?://'),
+	get_started_url VARCHAR(200) CHECK (get_started_url ~ '^https?://\S+'),
path: database/004-create-software-table.sql
user: login: ewan-escience
body: See above.
created_at: 2025-04-07T09:56:37Z
author_association: COLLABORATOR
diff_hunk: @@ -25,8 +26,8 @@ export const config={
     validation: {
       maxLength: {value: 200, message: 'Maximum length is 200'},
       pattern: {
-        value: /^https?:\/\/.+\..+/,
-        message: 'URL should start with http(s)://, have at least one dot (.) and at least one slash (/).'
+        value: /^https?:\/\/\S+\.\S+$/,
path: frontend/components/software/edit/links/config.tsx
user: login: ewan-escience
body: This is the only instance left that checks for a dot, can you change it?
created_at: 2025-04-11T08:42:08Z
author_association: COLLABORATOR
diff_hunk: @@ -25,8 +26,8 @@ export const config={
     validation: {
       maxLength: {value: 200, message: 'Maximum length is 200'},
       pattern: {
-        value: /^https?:\/\/.+\..+/,
-        message: 'URL should start with http(s)://, have at least one dot (.) and at least one slash (/).'
+        value: /^https?:\/\/\S+\/\S*$/,
path: frontend/components/software/edit/links/config.tsx
user: login: ewan-escience
body: I could have been more clear on this, but can you also skip the slash check? Just make it the same as all other checks. :)
created_at: 2025-04-11T12:41:23Z
author_association: COLLABORATOR

PR comments:
user: login: sonarqubecloud[bot]
created_at: 2025-04-14T05:43:19Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=rsd-database&pullRequest=1447) **Quality Gate passed for 'rsd-database'**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [1 New issue](https://sonarcloud.io/project/issues?id=rsd-database&pullRequest=1447&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=rsd-database&pullRequest=1447&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=rsd-database&pullRequest=1447&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=rsd-database&pullRequest=1447&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=rsd-database&pullRequest=1447&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=rsd-database&pullRequest=1447)


author_association: NONE
user: login: sonarqubecloud[bot]
created_at: 2025-04-14T05:45:22Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=rsd-frontend&pullRequest=1447) **Quality Gate passed for 'rsd-frontend'**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=rsd-frontend&pullRequest=1447&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=rsd-frontend&pullRequest=1447&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=rsd-frontend&pullRequest=1447&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=rsd-frontend&pullRequest=1447&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=rsd-frontend&pullRequest=1447&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=rsd-frontend&pullRequest=1447)


author_association: NONE
user: login: ewan-escience
created_at: 2025-04-14T16:51:48Z
body: @cmeessen can you approve this? The system still considers you to have requested changes, blocking the merge.
author_association: COLLABORATOR