]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
string-util: add STRING_DISALLOW_WHITESPACE for string_is_safe
authorLuca Boccassi <luca.boccassi@gmail.com>
Tue, 16 Jun 2026 20:55:33 +0000 (21:55 +0100)
committerLuca Boccassi <luca.boccassi@gmail.com>
Wed, 17 Jun 2026 12:11:15 +0000 (13:11 +0100)
Co-developed-by: Claude Opus 4.8 <noreply@anthropic.com>
src/basic/string-util.c
src/basic/string-util.h
src/test/test-string-util.c

index b972f7a42c92ef06c46f4fbbdcb15e20f4caca1c..cfeb2e3917afac2873cdbe3acecb1906c060d788 100644 (file)
@@ -1129,6 +1129,9 @@ bool string_is_safe(const char *p, StringSafeFlags flags) {
                 if (!FLAGS_SET(flags, STRING_ALLOW_GLOBS) && strchr(GLOB_CHARS, *t))
                         return false;
 
+                if (FLAGS_SET(flags, STRING_DISALLOW_WHITESPACE) && strchr(WHITESPACE, *t))
+                        return false;
+
                 if (FLAGS_SET(flags, STRING_ASCII) && (uint8_t) *t >= 0x80)
                         return false;
         }
index 447f4f807e76b858e8a642d2dc7a05f031cdc78d..6242f5593129575a2d3d325ea42b0ed6bceebca3 100644 (file)
@@ -222,13 +222,14 @@ static inline int strdup_to(char **ret, const char *src) {
 }
 
 typedef enum StringSafeFlags {
-        STRING_ASCII             = 1 << 0, /* Verify string is 7-Bit ASCII (rather than just UTF-8) */
-        STRING_ALLOW_EMPTY       = 1 << 1, /* Allow empty strings */
-        STRING_ALLOW_NEWLINES    = 1 << 2, /* Allow newlines (\n) */
-        STRING_ALLOW_BACKSLASHES = 1 << 3, /* Allow backslashes (\) */
-        STRING_ALLOW_QUOTES      = 1 << 4, /* Allow quotes (" or ') */
-        STRING_ALLOW_GLOBS       = 1 << 5, /* Allow globs (?, * or [) */
-        STRING_FILENAME          = 1 << 6, /* Verify the string is valid as regular filename */
+        STRING_ASCII               = 1 << 0, /* Verify string is 7-Bit ASCII (rather than just UTF-8) */
+        STRING_ALLOW_EMPTY         = 1 << 1, /* Allow empty strings */
+        STRING_ALLOW_NEWLINES      = 1 << 2, /* Allow newlines (\n) */
+        STRING_ALLOW_BACKSLASHES   = 1 << 3, /* Allow backslashes (\) */
+        STRING_ALLOW_QUOTES        = 1 << 4, /* Allow quotes (" or ') */
+        STRING_ALLOW_GLOBS         = 1 << 5, /* Allow globs (?, * or [) */
+        STRING_FILENAME            = 1 << 6, /* Verify the string is valid as regular filename */
+        STRING_DISALLOW_WHITESPACE = 1 << 7, /* Refuse whitespace (space, tab, newline, …) */
 } StringSafeFlags;
 
 bool string_is_safe(const char *p, StringSafeFlags flags) _pure_;
index 4a8656f41101e6c38a49fdc405a4120f184452cf..648b8fa839d0d43800b826e2ae99cd57fa8ac232 100644 (file)
@@ -1607,6 +1607,21 @@ TEST(string_is_safe) {
         ASSERT_FALSE(string_is_safe("/foo", STRING_FILENAME));
         ASSERT_FALSE(string_is_safe("foo/bar", STRING_FILENAME));
 
+        /* STRING_DISALLOW_WHITESPACE: rejects whitespace (space, tab, newline, carriage return). */
+        ASSERT_TRUE(string_is_safe("hello", STRING_DISALLOW_WHITESPACE));
+        ASSERT_TRUE(string_is_safe("foo-bar_baz", STRING_DISALLOW_WHITESPACE));
+        ASSERT_TRUE(string_is_safe("über", STRING_DISALLOW_WHITESPACE));     /* valid UTF-8 still allowed */
+        ASSERT_TRUE(string_is_safe("hello world", 0));                       /* space accepted by default */
+        ASSERT_FALSE(string_is_safe("hello world", STRING_DISALLOW_WHITESPACE)); /* but not with the flag */
+        ASSERT_FALSE(string_is_safe(" ", STRING_DISALLOW_WHITESPACE));
+        ASSERT_FALSE(string_is_safe("foo ", STRING_DISALLOW_WHITESPACE));
+        ASSERT_FALSE(string_is_safe(" foo", STRING_DISALLOW_WHITESPACE));
+        ASSERT_FALSE(string_is_safe("a\tb", STRING_DISALLOW_WHITESPACE));
+        ASSERT_FALSE(string_is_safe("a\rb", STRING_DISALLOW_WHITESPACE));
+        /* The flag overrides STRING_ALLOW_NEWLINES for the newline character, which is whitespace too. */
+        ASSERT_TRUE(string_is_safe("a\nb", STRING_ALLOW_NEWLINES));
+        ASSERT_FALSE(string_is_safe("a\nb", STRING_ALLOW_NEWLINES | STRING_DISALLOW_WHITESPACE));
+
         /* Pairwise combinations. */
         ASSERT_TRUE(string_is_safe("", STRING_ALLOW_EMPTY | STRING_ASCII));
         ASSERT_FALSE(string_is_safe("über", STRING_ALLOW_EMPTY | STRING_ASCII));