diff --git a/src/basic/ansi-color.c b/src/basic/ansi-color.c index 839dbc5e91..af2aa296d0 100644 --- a/src/basic/ansi-color.c +++ b/src/basic/ansi-color.c @@ -100,3 +100,28 @@ static const char* const color_mode_table[_COLOR_MODE_MAX] = { }; DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(color_mode, ColorMode, COLOR_24BIT); + +/* + * Check that the string is formatted like an ANSI color code, i.e. that it consists of one or more + * sequences of ASCII digits separated by semicolons. This is equivalent to matching the regex: + * ^[0-9]+(;[0-9]+)*$ + * This can be used to partially validate escape codes of the form "\x1B[%sm", accepting all valid + * ANSI color codes while rejecting anything that would result in garbled output (such as injecting + * text or changing the type of escape code). + */ +bool looks_like_ansi_color_code(const char *str) { + assert(str); + + bool prev_char_was_digit = false; + + for (char c = *str; c != '\0'; c = *(++str)) { + if (ascii_isdigit(c)) + prev_char_was_digit = true; + else if (prev_char_was_digit && c == ';') + prev_char_was_digit = false; + else + return false; + } + + return prev_char_was_digit; +} diff --git a/src/basic/ansi-color.h b/src/basic/ansi-color.h index a5eea226a9..c81120d299 100644 --- a/src/basic/ansi-color.h +++ b/src/basic/ansi-color.h @@ -28,6 +28,8 @@ bool underline_enabled(void); void reset_ansi_feature_caches(void); +bool looks_like_ansi_color_code(const char *str); + /* Regular colors */ #define ANSI_BLACK "\x1B[0;30m" /* Some type of grey usually. */ #define ANSI_RED "\x1B[0;31m" diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index bc1ed4c9eb..b19a04624b 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -1529,7 +1529,7 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_BACKGROUND: - r = free_and_strdup_warn(&arg_background, optarg); + r = parse_background_argument(optarg, &arg_background); if (r < 0) return r; break; diff --git a/src/ptyfwd/ptyfwd-tool.c b/src/ptyfwd/ptyfwd-tool.c index 62ef96a3dd..dd0c638f74 100644 --- a/src/ptyfwd/ptyfwd-tool.c +++ b/src/ptyfwd/ptyfwd-tool.c @@ -9,6 +9,7 @@ #include "fd-util.h" #include "log.h" #include "main-func.h" +#include "parse-argument.h" #include "pidref.h" #include "pretty-print.h" #include "process-util.h" @@ -96,7 +97,7 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_BACKGROUND: - r = free_and_strdup_warn(&arg_background, optarg); + r = parse_background_argument(optarg, &arg_background); if (r < 0) return r; break; diff --git a/src/run/run.c b/src/run/run.c index ae2bd4115e..1c01d4485b 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -686,7 +686,7 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_BACKGROUND: - r = free_and_strdup_warn(&arg_background, optarg); + r = parse_background_argument(optarg, &arg_background); if (r < 0) return r; break; @@ -978,7 +978,7 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) { break; case ARG_BACKGROUND: - r = free_and_strdup_warn(&arg_background, optarg); + r = parse_background_argument(optarg, &arg_background); if (r < 0) return r; diff --git a/src/shared/parse-argument.c b/src/shared/parse-argument.c index 58b914f241..f8cd7f30ff 100644 --- a/src/shared/parse-argument.c +++ b/src/shared/parse-argument.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "alloc-util.h" +#include "ansi-color.h" #include "bus-util.h" #include "format-table.h" #include "hostname-util.h" @@ -163,3 +164,10 @@ int parse_machine_argument(const char *s, const char **ret_host, BusTransport *r return 0; } + +int parse_background_argument(const char *s, char **arg) { + if (!isempty(s) && !looks_like_ansi_color_code(s)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid --background= argument: %s", s); + + return free_and_strdup_warn(arg, s); +} diff --git a/src/shared/parse-argument.h b/src/shared/parse-argument.h index f1b2b63178..75098b8eb1 100644 --- a/src/shared/parse-argument.h +++ b/src/shared/parse-argument.h @@ -9,3 +9,4 @@ int parse_json_argument(const char *s, sd_json_format_flags_t *ret); int parse_path_argument(const char *path, bool suppress_root, char **arg); int parse_signal_argument(const char *s, int *ret); int parse_machine_argument(const char *s, const char **ret_host, BusTransport *ret_transport); +int parse_background_argument(const char *s, char **arg); diff --git a/src/test/test-parse-argument.c b/src/test/test-parse-argument.c index 843179b666..b4eb8b1033 100644 --- a/src/test/test-parse-argument.c +++ b/src/test/test-parse-argument.c @@ -54,4 +54,37 @@ TEST(parse_signal_argument) { assert_se(signal == SIGABRT); } +TEST(parse_background_argument) { + _cleanup_free_ char *arg_bg_good = NULL; + + /* Should accept empty string */ + assert_se(parse_background_argument("", &arg_bg_good) >= 0); + ASSERT_STREQ(arg_bg_good, ""); + + /* Should accept ANSI color codes in palette, 8-bit, or 24-bit format */ + assert_se(parse_background_argument("42", &arg_bg_good) >= 0); + ASSERT_STREQ(arg_bg_good, "42"); + + assert_se(parse_background_argument("48;5;219", &arg_bg_good) >= 0); + ASSERT_STREQ(arg_bg_good, "48;5;219"); + + assert_se(parse_background_argument("48;2;3;141;59", &arg_bg_good) >= 0); + ASSERT_STREQ(arg_bg_good, "48;2;3;141;59"); + + _cleanup_free_ char *arg_bg_bad = NULL; + + /* Should reject various invalid arguments */ + assert_se(parse_background_argument("42;", &arg_bg_bad) < 0); + ASSERT_NULL(arg_bg_bad); + + assert_se(parse_background_argument(";42", &arg_bg_bad) < 0); + ASSERT_NULL(arg_bg_bad); + + assert_se(parse_background_argument("4;;2", &arg_bg_bad) < 0); + ASSERT_NULL(arg_bg_bad); + + assert_se(parse_background_argument("4a2", &arg_bg_bad) < 0); + ASSERT_NULL(arg_bg_bad); +} + DEFINE_TEST_MAIN(LOG_INFO); diff --git a/src/vmspawn/vmspawn.c b/src/vmspawn/vmspawn.c index 3a2cd919b6..f3ee2c28e7 100644 --- a/src/vmspawn/vmspawn.c +++ b/src/vmspawn/vmspawn.c @@ -612,7 +612,7 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_BACKGROUND: - r = free_and_strdup_warn(&arg_background, optarg); + r = parse_background_argument(optarg, &arg_background); if (r < 0) return r; break;