]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
oci-util: fix and harden oci_registry_is_valid()
authordongshengyuan <545258830@qq.com>
Mon, 22 Jun 2026 05:16:32 +0000 (13:16 +0800)
committerLennart Poettering <lennart@poettering.net>
Mon, 22 Jun 2026 19:59:15 +0000 (21:59 +0200)
- Pass colon+1 (port string) instead of s (hostname) to safe_atou16,
  so host:port registries are no longer always rejected.
- Switch to safe_atou16_full() with base-10 and strict flags to reject
  non-decimal port forms (hex, octal, leading whitespace, sign prefix)
  that would produce malformed URL authorities.
- Reject empty host explicitly via isempty() guard (covers both NULL
  and empty-string input), and guard colon == n to reject ':port' form,
  since dns_name_is_valid('') == 1 (DNS root) would otherwise accept
  empty host as valid.
- Wrap overlong line to fit 109-column limit.
- Add test coverage for oci_registry_is_valid().

Signed-off-by: dongshengyuan <dongshengyuan@uniontech.com>
src/import/oci-util.c
src/import/test-oci-util.c

index d8c16bc3d9785c7591b890edd55d2f96efa2d9c7..a3c5782e98938505b32912c9e38054297de51df9 100644 (file)
@@ -51,13 +51,16 @@ bool oci_image_is_valid(const char *n) {
 int oci_registry_is_valid(const char *n) {
         int r;
 
-        if (!n)
+        if (isempty(n))
                 return false;
 
         const char *colon = strchr(n, ':');
         if (!colon)
                 return dns_name_is_valid(n);
 
+        if (colon == n)  /* empty host, e.g. ":5000" */
+                return false;
+
         _cleanup_free_ char *s = strndup(n, colon - n);
         if (!s)
                 return -ENOMEM;
@@ -67,7 +70,10 @@ int oci_registry_is_valid(const char *n) {
                 return r;
 
         uint16_t port;
-        return safe_atou16(s, &port) >= 0 && port != 0;
+        return safe_atou16_full(colon + 1,
+                                10 | SAFE_ATO_REFUSE_LEADING_WHITESPACE |
+                                SAFE_ATO_REFUSE_PLUS_MINUS | SAFE_ATO_REFUSE_LEADING_ZERO,
+                                &port) >= 0 && port != 0;
 }
 
 bool oci_tag_is_valid(const char *n) {
index 395b622b42804a49e00ad7142e061ca07ccf98a3..3fa62eb1b5b4eb4c697461b06985f4714369dac6 100644 (file)
@@ -19,4 +19,33 @@ TEST(urlescape) {
         test_urlescape_one("müffel", "m%c3%bcffel");
 }
 
+TEST(oci_registry_is_valid) {
+        /* plain hostname — valid */
+        assert_se(oci_registry_is_valid("localhost") > 0);
+        assert_se(oci_registry_is_valid("registry.example.com") > 0);
+
+        /* host:port — valid */
+        assert_se(oci_registry_is_valid("localhost:5000") > 0);
+        assert_se(oci_registry_is_valid("registry.example.com:443") > 0);
+        assert_se(oci_registry_is_valid("registry.io:1") > 0);
+        assert_se(oci_registry_is_valid("registry.io:65535") > 0);
+
+        /* port 0 — invalid */
+        assert_se(oci_registry_is_valid("localhost:0") == 0);
+
+        /* port overflow */
+        assert_se(oci_registry_is_valid("localhost:65536") == 0);
+
+        /* non-decimal port forms — rejected */
+        assert_se(oci_registry_is_valid("localhost:0x50") == 0);   /* hex */
+        assert_se(oci_registry_is_valid("localhost:017") == 0);    /* leading zero */
+        assert_se(oci_registry_is_valid("localhost:+80") == 0);    /* plus sign */
+        assert_se(oci_registry_is_valid("localhost: 80") == 0);    /* leading space */
+
+        /* invalid hostname */
+        assert_se(oci_registry_is_valid(":5000") == 0);
+        assert_se(oci_registry_is_valid("") == 0);
+        assert_se(oci_registry_is_valid(NULL) == 0);
+}
+
 DEFINE_TEST_MAIN(LOG_DEBUG);