public inbox for pdm-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Thomas Ellmenreich <t.ellmenreich@proxmox.com>
To: pdm-devel@lists.proxmox.com
Cc: Thomas Ellmenreich <t.ellmenreich@proxmox.com>
Subject: [PATCH proxmox 3/4] api-types: add a scheme generic URL regex
Date: Thu, 13 Aug 2026 17:05:50 +0200	[thread overview]
Message-ID: <20260813150551.415237-4-t.ellmenreich@proxmox.com> (raw)
In-Reply-To: <20260813150551.415237-1-t.ellmenreich@proxmox.com>

The current URL regex, HTTP_URL_REGEX, is specific to the HTTP and HTTPS
schemes. This patch creates a new regex that is scheme-generic and thus
allows any other valid scheme.

It also adds tests that cover some basic URL edge cases.

Signed-off-by: Thomas Ellmenreich <t.ellmenreich@proxmox.com>
---
 pbs-api-types/src/lib.rs        |  1 +
 proxmox-schema/src/api_types.rs | 40 ++++++++++++++++++++++++++++++++-
 2 files changed, 40 insertions(+), 1 deletion(-)

diff --git a/pbs-api-types/src/lib.rs b/pbs-api-types/src/lib.rs
index 21ef733d..924f356b 100644
--- a/pbs-api-types/src/lib.rs
+++ b/pbs-api-types/src/lib.rs
@@ -44,6 +44,7 @@ pub use proxmox_schema::api_types::HTTP_URL_SCHEMA;
 pub use proxmox_schema::api_types::MULTI_LINE_COMMENT_SCHEMA;
 pub use proxmox_schema::api_types::NODE_SCHEMA;
 pub use proxmox_schema::api_types::SINGLE_LINE_COMMENT_FORMAT;
+pub use proxmox_schema::api_types::URL_SCHEMA;
 pub use proxmox_schema::api_types::{
     BLOCKDEVICE_DISK_AND_PARTITION_NAME_SCHEMA, BLOCKDEVICE_NAME_SCHEMA,
 };
diff --git a/proxmox-schema/src/api_types.rs b/proxmox-schema/src/api_types.rs
index 81878dbe..05e50f26 100644
--- a/proxmox-schema/src/api_types.rs
+++ b/proxmox-schema/src/api_types.rs
@@ -50,6 +50,10 @@ pub const CIDR_V6_REGEX_STR: &str = concatcp!(r"(?:", IPV6RE_STR, r"/\d{1,3})$")
 #[rustfmt::skip]
 pub const SAFE_ID_REGEX_STR: &str = r"(?:[A-Za-z0-9_][A-Za-z0-9._\-]*)";
 
+/// Regular expression string to match allowed URL schemes.
+#[rustfmt::skip]
+pub const URL_SCHEME_REGEX_STR: &str = r"(?:[a-zA-Z][a-zA-Z0-9\+.\-]*)";
+
 /// Regular expression string to match a host, optional port and optionally a
 /// path, although the path remains completely unvalidated except a few
 /// prohibited characters.
@@ -113,6 +117,12 @@ const_regex! {
     pub DNS_NAME_OR_IP_REGEX = concatcp!(r"^(?:", DNS_NAME_STR, "|",  IPRE_STR, r")$");
     pub HOST_PORT_REGEX = concatcp!(r"^(?:", DNS_NAME_STR, "|", IPRE_BRACKET_STR, "):", PORT_REGEX_STR ,"$");
 
+    /// Matches URL constructed out of a scheme, host, port, and path. With
+    /// the host possibly being an IPv[4|6] address and the port being
+    /// optional. The path remains completely unvalidated except for a few
+    /// prohibited characters.
+    pub URL_REGEX = concatcp!(r"^", URL_SCHEME_REGEX_STR, "://", URL_HOST_PORT_PATH_REGEX_STR, "$");
+
     /// A specialisation of [`URL_REGEX`] regex that only allows http and
     /// https as the URL scheme
     pub HTTP_URL_REGEX = concatcp!(r"^https?://", URL_HOST_PORT_PATH_REGEX_STR, "$");
@@ -166,6 +176,7 @@ pub const SYSTEMD_DATETIME_FORMAT: ApiStringFormat =
 pub const HOSTNAME_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&HOSTNAME_REGEX);
 pub const HOST_PORT_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&HOST_PORT_REGEX);
 pub const HTTP_URL_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&HTTP_URL_REGEX);
+pub const URL_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&URL_REGEX);
 
 pub const DNS_ALIAS_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&DNS_ALIAS_REGEX);
 pub const DNS_NAME_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&DNS_NAME_REGEX);
@@ -243,6 +254,10 @@ pub const PORT_SCHEMA: Schema = IntegerSchema::new("Node port")
     .maximum(65535)
     .schema();
 
+pub const URL_SCHEMA: Schema = StringSchema::new("Url with optional port.")
+    .format(&URL_FORMAT)
+    .schema();
+
 pub const HTTP_URL_SCHEMA: Schema = StringSchema::new("HTTP(s) url with optional port.")
     .format(&HTTP_URL_FORMAT)
     .schema();
@@ -286,7 +301,7 @@ pub const DISK_LIST_SCHEMA: Schema = StringSchema::new("A list of disk names, co
 mod tests {
     use super::{
         CIDR_REGEX, CIDR_V4_REGEX, CIDR_V6_REGEX, ED25519_BASE64_KEY_REGEX, IP_BRACKET_REGEX,
-        IP_REGEX, IP_V4_REGEX, IP_V6_REGEX,
+        IP_REGEX, IP_V4_REGEX, IP_V6_REGEX, URL_REGEX,
     };
 
     #[test]
@@ -327,4 +342,27 @@ mod tests {
         // 33 bytes of data
         assert!(!ED25519_BASE64_KEY_REGEX.is_match("IiC3Nkh4Fn2ukUZUNmdK5K5CWO53Zmk/eGlKO4m6aCD/"));
     }
+
+    #[test]
+    fn test_url_regex_on_valid_urls() {
+        assert!(URL_REGEX.is_match("https://www.example.com/index.html"));
+        assert!(URL_REGEX.is_match("https://[2001:db8::1]/docs/%E2%9C%93.html?lang=en"));
+        assert!(URL_REGEX.is_match("ftp://ftp.example.com/pub/archive/file.zip"));
+        assert!(URL_REGEX.is_match("file://test.com/home/alice/documents/report.pdf"));
+        assert!(URL_REGEX.is_match("https://example.com:443/a%20file.html?name=John%20Doe#top"));
+    }
+
+    #[test]
+    fn test_url_regex_on_invalid_urls() {
+        // is missing the ':' after the scheme
+        assert!(!URL_REGEX.is_match("https//www.example.com/index.html"));
+        // has two port numbers
+        assert!(!URL_REGEX.is_match("http://example.com:80:90/page"));
+        // only has one '/' after the scheme
+        assert!(!URL_REGEX.is_match("ftp:/ftp.example.com/file.txt"));
+        // has userinfo (technically correct, but not accepted by the regex)
+        assert!(!URL_REGEX.is_match("mailto:alice@example.com"));
+        // missing closing angle ']' brachet
+        assert!(!URL_REGEX.is_match("https://[2001:db8::1/path"));
+    }
 }
-- 
2.47.3





  parent reply	other threads:[~2026-08-13 15:06 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-13 15:05 [PATCH datacenter-manager/proxmox 0/4] fix #7747: OpenID: allow non HTTP scheme in redirect URL Thomas Ellmenreich
2026-08-13 15:05 ` [PATCH proxmox 1/4] api-types: reorganise unit tests Thomas Ellmenreich
2026-08-13 15:05 ` [PATCH proxmox 2/4] api-types: refactor HTTP_URL_REGEX construction Thomas Ellmenreich
2026-08-13 15:05 ` Thomas Ellmenreich [this message]
2026-08-13 15:05 ` [PATCH datacenter-manager 4/4] fix #7747: openid: allow non HTTP schemes in redirect URL Thomas Ellmenreich

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260813150551.415237-4-t.ellmenreich@proxmox.com \
    --to=t.ellmenreich@proxmox.com \
    --cc=pdm-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal