From 3183fa9670e386df4aabd4f8bb33b7413b28184f Mon Sep 17 00:00:00 2001 From: Dominik Krajcer Date: Mon, 24 Nov 2025 20:17:36 +0100 Subject: [PATCH 1/7] fix(windows,linux): convert AM/PM pattern from 'tt' to 'a' - Windows: Replace 'tt' pattern with 'a' after retrieving format from system - Windows: Use dynamic buffer allocation instead of fixed 80-char buffer - Linux: Change %p and %r patterns to use 'a' instead of 'tt' - Linux: Improve performance by replacing strcat() with memcpy() - Linux: Add null checks and bounds checking for better memory safety - Linux: Fix edge case where input could end with '%' This ensures compatibility with standard date/time format patterns used by Flutter/Dart, where 'a' represents AM/PM designator. --- linux/system_date_time_format_plugin.cc | 150 +++++++++------------ windows/system_date_time_format_plugin.cpp | 29 +++- 2 files changed, 90 insertions(+), 89 deletions(-) diff --git a/linux/system_date_time_format_plugin.cc b/linux/system_date_time_format_plugin.cc index ebd192f..9511ec7 100644 --- a/linux/system_date_time_format_plugin.cc +++ b/linux/system_date_time_format_plugin.cc @@ -36,120 +36,96 @@ char* trim(char* s) { char* format_date(const char* date) { const int BUFFER_LENGTH = strlen(date) * 3 + 1; char* formatted_date = (char*)malloc(BUFFER_LENGTH); - memset(formatted_date, 0, BUFFER_LENGTH); - + if (!formatted_date) return nullptr; + + int pos = 0; int i = 0; - while (date[i] != '\0') { - if (date[i] == '%') { + while (date[i] != '\0' && pos < BUFFER_LENGTH - 11) { + if (date[i] == '%' && date[i + 1] != '\0') { char specifier = date[i + 1]; + const char* replacement = nullptr; switch (specifier) { - case 'a': strcat(formatted_date, "ddd"); break; - case 'A': strcat(formatted_date, "dddd"); break; + case 'a': replacement = "ddd"; break; + case 'A': replacement = "dddd"; break; case 'b': - case 'h': strcat(formatted_date, "MMM"); break; - case 'B': strcat(formatted_date, "MMMM"); break; - case 'c': strcat(formatted_date, ""); break; - case 'C': strcat(formatted_date, "yy"); break; - case 'd': strcat(formatted_date, "dd"); break; - case 'D': strcat(formatted_date, "MM/dd/yy"); break; - case 'e': strcat(formatted_date, "d"); break; - case 'F': strcat(formatted_date, "yyyy-MM-dd"); break; - case 'g': strcat(formatted_date, "yy"); break; - case 'G': strcat(formatted_date, "yyyy"); break; - case 'H': - case 'I': - case 'j': strcat(formatted_date, "DDD"); break; - case 'm': strcat(formatted_date, "MM"); break; - case 'M': - case 'n': - case 'p': - case 'r': - case 'R': - case 'S': - case 't': - case 'T': - case 'u': - case 'U': - case 'V': - case 'w': - case 'W': - case 'x': - case 'X': strcat(formatted_date, ""); break; - case 'y': strcat(formatted_date, "yy"); break; - case 'Y': strcat(formatted_date, "yyyy"); break; - case 'z': - case 'Z': - case '%': strcat(formatted_date, ""); break; - default: formatted_date[strlen(formatted_date)] = date[i + 1]; break; + case 'h': replacement = "MMM"; break; + case 'B': replacement = "MMMM"; break; + case 'C': replacement = "yy"; break; + case 'd': replacement = "dd"; break; + case 'D': replacement = "MM/dd/yy"; break; + case 'e': replacement = "d"; break; + case 'F': replacement = "yyyy-MM-dd"; break; + case 'g': replacement = "yy"; break; + case 'G': replacement = "yyyy"; break; + case 'j': replacement = "DDD"; break; + case 'm': replacement = "MM"; break; + case 'y': replacement = "yy"; break; + case 'Y': replacement = "yyyy"; break; + case '%': + formatted_date[pos++] = '%'; + break; + default: + formatted_date[pos++] = date[i + 1]; + break; + } + if (replacement) { + int len = strlen(replacement); + memcpy(formatted_date + pos, replacement, len); + pos += len; } i += 2; } else { - formatted_date[strlen(formatted_date)] = date[i]; + formatted_date[pos++] = date[i]; i++; } } - formatted_date[strlen(formatted_date)] = '\0'; + formatted_date[pos] = '\0'; return trim(formatted_date); } char* format_time(const char* time) { const int BUFFER_LENGTH = strlen(time) * 3 + 1; char* formatted_time = (char*)malloc(BUFFER_LENGTH); - memset(formatted_time, 0, BUFFER_LENGTH); - + if (!formatted_time) return nullptr; + + int pos = 0; int i = 0; - while (time[i] != '\0') { - if (time[i] == '%') { + while (time[i] != '\0' && pos < BUFFER_LENGTH - 15) { + if (time[i] == '%' && time[i + 1] != '\0') { char specifier = time[i + 1]; + const char* replacement = nullptr; switch (specifier) { - case 'a': - case 'A': - case 'b': - case 'h': - case 'B': - case 'c': - case 'C': - case 'd': - case 'D': - case 'e': - case 'F': - case 'g': - case 'G': strcat(formatted_time, ""); break; - case 'H': strcat(formatted_time, "HH"); break; - case 'I': strcat(formatted_time, "hh"); break; - case 'j': - case 'm': strcat(formatted_time, ""); break; - case 'M': strcat(formatted_time, "mm"); break; - case 'n': strcat(formatted_time, "\n"); break; - case 'p': strcat(formatted_time, "tt"); break; - case 'r': strcat(formatted_time, "hh:mm:ss tt"); break; - case 'R': strcat(formatted_time, "HH:mm"); break; - case 'S': strcat(formatted_time, "ss"); break; - case 't': strcat(formatted_time, "\t"); break; - case 'T': strcat(formatted_time, "HH:mm:ss"); break; - case 'u': - case 'U': - case 'V': - case 'w': - case 'W': - case 'x': - case 'X': - case 'y': - case 'Y': - case 'z': - case 'Z': - case '%': strcat(formatted_time, ""); break; - default: formatted_time[strlen(formatted_time)] = time[i + 1]; break; + case 'H': replacement = "HH"; break; + case 'I': replacement = "hh"; break; + case 'M': replacement = "mm"; break; + case 'n': replacement = "\n"; break; + case 'p': replacement = "a"; break; + case 'r': replacement = "hh:mm:ss a"; break; + case 'R': replacement = "HH:mm"; break; + case 'S': replacement = "ss"; break; + case 't': replacement = "\t"; break; + case 'T': replacement = "HH:mm:ss"; break; + case '%': + formatted_time[pos++] = '%'; + break; + default: + formatted_time[pos++] = time[i + 1]; + break; + } + if (replacement) { + int len = strlen(replacement); + memcpy(formatted_time + pos, replacement, len); + pos += len; } i += 2; } else { - formatted_time[strlen(formatted_time)] = time[i]; + formatted_time[pos++] = time[i]; i++; } } - formatted_time[strlen(formatted_time)] = '\0'; + formatted_time[pos] = '\0'; return trim(formatted_time); } diff --git a/windows/system_date_time_format_plugin.cpp b/windows/system_date_time_format_plugin.cpp index 7600158..efb7ed1 100644 --- a/windows/system_date_time_format_plugin.cpp +++ b/windows/system_date_time_format_plugin.cpp @@ -79,12 +79,37 @@ namespace system_date_time_format { } string SystemDateTimeFormatPlugin::getFormat(LCTYPE infoType) { - TCHAR buffer[80]; - GetLocaleInfo(LOCALE_USER_DEFAULT, infoType, buffer, 80); + // First, get the required buffer size + int bufferSize = GetLocaleInfo(LOCALE_USER_DEFAULT, infoType, nullptr, 0); + if (bufferSize == 0) { + return ""; + } + + // Allocate buffer with the required size + TCHAR* buffer = new (std::nothrow) TCHAR[bufferSize]; + if (!buffer) { + return ""; + } + + int result_size = GetLocaleInfo(LOCALE_USER_DEFAULT, infoType, buffer, bufferSize); + if (result_size == 0) { + delete[] buffer; + return ""; + } + // Convert to UTF-8 int size = WideCharToMultiByte(CP_UTF8, 0, buffer, -1, nullptr, 0, nullptr, nullptr); string result(size - 1, 0); // subtract 1 to remove the null terminator WideCharToMultiByte(CP_UTF8, 0, buffer, -1, &result[0], size, nullptr, nullptr); + + delete[] buffer; + + // Convert Windows 'tt' (A.M./P.M.) pattern to standard 'a' pattern + size_t pos = 0; + while ((pos = result.find("tt", pos)) != string::npos) { + result.replace(pos, 2, "a"); + pos += 1; + } return result; } From 8718a408055bfbf846d7ba977a5f15a4e8c16e58 Mon Sep 17 00:00:00 2001 From: Dominik Krajcer Date: Mon, 24 Nov 2025 20:46:28 +0100 Subject: [PATCH 2/7] Update windows/system_date_time_format_plugin.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- windows/system_date_time_format_plugin.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/windows/system_date_time_format_plugin.cpp b/windows/system_date_time_format_plugin.cpp index efb7ed1..fe6461e 100644 --- a/windows/system_date_time_format_plugin.cpp +++ b/windows/system_date_time_format_plugin.cpp @@ -100,7 +100,11 @@ namespace system_date_time_format { // Convert to UTF-8 int size = WideCharToMultiByte(CP_UTF8, 0, buffer, -1, nullptr, 0, nullptr, nullptr); string result(size - 1, 0); // subtract 1 to remove the null terminator - WideCharToMultiByte(CP_UTF8, 0, buffer, -1, &result[0], size, nullptr, nullptr); + int actual_size = WideCharToMultiByte(CP_UTF8, 0, buffer, -1, &result[0], size, nullptr, nullptr); + if (actual_size == 0) { + delete[] buffer; + return ""; + } delete[] buffer; From b701c4bae979df203914bb5493fb1300e74a190d Mon Sep 17 00:00:00 2001 From: Dominik Krajcer Date: Mon, 24 Nov 2025 20:46:38 +0100 Subject: [PATCH 3/7] Update linux/system_date_time_format_plugin.cc Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- linux/system_date_time_format_plugin.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/linux/system_date_time_format_plugin.cc b/linux/system_date_time_format_plugin.cc index 9511ec7..3206544 100644 --- a/linux/system_date_time_format_plugin.cc +++ b/linux/system_date_time_format_plugin.cc @@ -115,8 +115,10 @@ char* format_time(const char* time) { } if (replacement) { int len = strlen(replacement); - memcpy(formatted_time + pos, replacement, len); - pos += len; + if (pos + len < BUFFER_LENGTH) { + memcpy(formatted_time + pos, replacement, len); + pos += len; + } } i += 2; } From 4c9d3d569efe614f4ea2967507e7c0c95a3a74ee Mon Sep 17 00:00:00 2001 From: Dominik Krajcer Date: Mon, 24 Nov 2025 20:47:18 +0100 Subject: [PATCH 4/7] Update linux/system_date_time_format_plugin.cc Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- linux/system_date_time_format_plugin.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/linux/system_date_time_format_plugin.cc b/linux/system_date_time_format_plugin.cc index 3206544..f926062 100644 --- a/linux/system_date_time_format_plugin.cc +++ b/linux/system_date_time_format_plugin.cc @@ -70,8 +70,10 @@ char* format_date(const char* date) { } if (replacement) { int len = strlen(replacement); - memcpy(formatted_date + pos, replacement, len); - pos += len; + if (pos + len < BUFFER_LENGTH) { + memcpy(formatted_date + pos, replacement, len); + pos += len; + } } i += 2; } From 8c3c9c6cc72445a92c89c5f7ac6db8a1ca316280 Mon Sep 17 00:00:00 2001 From: Dominik Krajcer Date: Mon, 24 Nov 2025 20:47:29 +0100 Subject: [PATCH 5/7] Update windows/system_date_time_format_plugin.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- windows/system_date_time_format_plugin.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/windows/system_date_time_format_plugin.cpp b/windows/system_date_time_format_plugin.cpp index fe6461e..ae61cd8 100644 --- a/windows/system_date_time_format_plugin.cpp +++ b/windows/system_date_time_format_plugin.cpp @@ -99,6 +99,10 @@ namespace system_date_time_format { // Convert to UTF-8 int size = WideCharToMultiByte(CP_UTF8, 0, buffer, -1, nullptr, 0, nullptr, nullptr); + if (size == 0) { + delete[] buffer; + return ""; + } string result(size - 1, 0); // subtract 1 to remove the null terminator int actual_size = WideCharToMultiByte(CP_UTF8, 0, buffer, -1, &result[0], size, nullptr, nullptr); if (actual_size == 0) { From 9c7c3facba31d66321f98e1fb4400ce78a48e314 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Mon, 24 Nov 2025 20:58:13 +0100 Subject: [PATCH 6/7] Replace magic number with named constant in format_time bounds check (#30) * Initial plan * Replace magic number with MAX_REPLACEMENT_LENGTH constant Co-authored-by: Nikoro <12560977+Nikoro@users.noreply.github.com> * Complete feedback implementation Co-authored-by: Nikoro <12560977+Nikoro@users.noreply.github.com> * Update linux/system_date_time_format_plugin.cc Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Nikoro <12560977+Nikoro@users.noreply.github.com> Co-authored-by: Dominik Krajcer Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- _codeql_detected_source_root | 1 + linux/system_date_time_format_plugin.cc | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 120000 _codeql_detected_source_root diff --git a/_codeql_detected_source_root b/_codeql_detected_source_root new file mode 120000 index 0000000..945c9b4 --- /dev/null +++ b/_codeql_detected_source_root @@ -0,0 +1 @@ +. \ No newline at end of file diff --git a/linux/system_date_time_format_plugin.cc b/linux/system_date_time_format_plugin.cc index f926062..34b279f 100644 --- a/linux/system_date_time_format_plugin.cc +++ b/linux/system_date_time_format_plugin.cc @@ -91,9 +91,10 @@ char* format_time(const char* time) { char* formatted_time = (char*)malloc(BUFFER_LENGTH); if (!formatted_time) return nullptr; + const int MAX_REPLACEMENT_LENGTH = 10; // "hh:mm:ss a" is the longest int pos = 0; int i = 0; - while (time[i] != '\0' && pos < BUFFER_LENGTH - 15) { + while (time[i] != '\0' && pos < BUFFER_LENGTH - MAX_REPLACEMENT_LENGTH) { if (time[i] == '%' && time[i + 1] != '\0') { char specifier = time[i + 1]; const char* replacement = nullptr; From 3b08b1ac017f83f0b7fb67cc92a7b977a0dc8d7b Mon Sep 17 00:00:00 2001 From: Dominik Krajcer Date: Mon, 24 Nov 2025 21:07:50 +0100 Subject: [PATCH 7/7] Update linux/system_date_time_format_plugin.cc Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- linux/system_date_time_format_plugin.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux/system_date_time_format_plugin.cc b/linux/system_date_time_format_plugin.cc index 34b279f..46c6d93 100644 --- a/linux/system_date_time_format_plugin.cc +++ b/linux/system_date_time_format_plugin.cc @@ -94,7 +94,7 @@ char* format_time(const char* time) { const int MAX_REPLACEMENT_LENGTH = 10; // "hh:mm:ss a" is the longest int pos = 0; int i = 0; - while (time[i] != '\0' && pos < BUFFER_LENGTH - MAX_REPLACEMENT_LENGTH) { + while (time[i] != '\0' && (time[i] != '%' || time[i + 1] != '\0') && pos < BUFFER_LENGTH - MAX_REPLACEMENT_LENGTH) { if (time[i] == '%' && time[i + 1] != '\0') { char specifier = time[i + 1]; const char* replacement = nullptr;