From: Michael Tremer Date: Fri, 14 Feb 2025 17:04:32 +0000 (+0000) Subject: pty: Move filtering escape characters to the PTY X-Git-Tag: 0.9.30~38 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=8328b86e77704c11e60e53995c9c6488d16bc268;p=pakfire.git pty: Move filtering escape characters to the PTY This is probably the place where this should live. We are now processing cursor positioning events inside the same line and we are filtering out any escape sequences that we don't know. It is a bit sad to lose the colors, but I don't have to have two places where we process this. At least this is going to be more robust and we can be sure to not forward anything malicious to the controlling terminal. Signed-off-by: Michael Tremer --- diff --git a/src/pakfire/log_file.c b/src/pakfire/log_file.c index 2733d946..7a37a075 100644 --- a/src/pakfire/log_file.c +++ b/src/pakfire/log_file.c @@ -188,104 +188,7 @@ int pakfire_log_file_close(struct pakfire_log_file* self) { return 0; } -#define pakfire_log_file_process_line(file, line, buffer) \ - __pakfire_log_file_process_line(file, line, sizeof(line), buffer) - -static int __pakfire_log_file_process_line(struct pakfire_log_file* self, - char* line, ssize_t length, const char* p) { - char* e = NULL; - int num; - - // The cursor position - int cursor = 0; - - while (*p) { - // Match the beginning of an ESC sequence - switch (*p) { - case '\x1B': - p++; - - // Match CSI sequences - switch (*p) { - case '[': - p++; - - // Parse the number - num = strtoul(p, &e, 10); - - // Continue from after the number - p = e; - - switch (*p) { - // Move the cursor to the left - case 'D': - cursor -= (num > 0) ? num : 1; - - // Don't go too far left - if (cursor < 0) - cursor = 0; - break; - - // Move the cursor to the right - case 'C': - cursor += (num > 0) ? num : 1; - - // Don't go too far right - if (cursor >= length) - cursor = length - 1; - break; - - // Move to a certain column - case 'G': - cursor = (num > 0) ? num - 1 : 0; - - // Don't go too far right - if (cursor >= length) - cursor = length - 1; - break; - } - - // Skip the final character - p++; - break; - } - break; - - // Backspace - case '\x08': - if (cursor > 0) { - cursor--; - - // Erase the previous character - line[cursor] = ' '; - } - p++; - break; - - // Carriage Return - case '\x0d': - // Erase everything up to the cursor - for (int i = 0; i < cursor; i++) - line[i] = '\0'; - - cursor = 0; - p++; - break; - - // Normal characters - default: - if (cursor < length - 1) - line[cursor++] = *p; - p++; - break; - } - } - - return 0; -} - int pakfire_log_file_write(struct pakfire_log_file* self, const char* buffer, ssize_t length) { - char line[MAX_LINE_LENGTH] = {}; int r; // Check inputs @@ -296,14 +199,13 @@ int pakfire_log_file_write(struct pakfire_log_file* self, const char* buffer, ss if (unlikely(!self->f)) return -EBADF; - // Process any ANSI Escape codes - r = pakfire_log_file_process_line(self, line, buffer); - if (r < 0) - return r; + // Automatically determine the length + if (unlikely(length < 0)) + length = strlen(buffer); // Write the buffer to the file - r = fprintf(self->f, "%s", line); - if (unlikely(r < 0)) { + r = fwrite(buffer, 1, length, self->f); + if (unlikely(r < length)) { ERROR(self->ctx, "Could not write to log file: %m\n"); return -errno; } diff --git a/src/pakfire/pty.c b/src/pakfire/pty.c index 30d6f26f..85226676 100644 --- a/src/pakfire/pty.c +++ b/src/pakfire/pty.c @@ -36,13 +36,15 @@ #include #include +#define MAX_LINE_LENGTH 4096 + struct pakfire_pty_stdio { // File Descriptor int fd; unsigned close_fd:1; // Buffer - char buffer[4096]; + char buffer[MAX_LINE_LENGTH]; size_t buffered; // Terminal Attributes @@ -423,6 +425,141 @@ static int pakfire_pty_fill_buffer(struct pakfire_pty* pty, int fd, struct pakfi return bytes_read; } +#define pakfire_pty_sanitize_line(pty, line, buffer, buffer_length) \ + __pakfire_pty_sanitize_line(pty, line, sizeof(line), buffer, buffer_length) + +static int __pakfire_pty_sanitize_line(struct pakfire_pty* self, + char* line, ssize_t line_length, const char* buffer, ssize_t length) { + ssize_t i = 0; + int num; + + // The cursor position + int cursor = 0; + + while (i < length) { + switch (buffer[i]) { + // Escape + case '\x1b': + // Skip the escape character + i++; + + // CSI + if (i < length && buffer[i] == '[') { + i++; + + // Reset number + num = 0; + + // Parse the number + while (i < length) { + // Skip any ';' + if (buffer[i] == ';') { + i++; + continue; + + // Parse any digits + } else if (isdigit(buffer[i])) { + num = num * 10 + (buffer[i] - '0'); + i++; + continue; + } + + // Break on all other characters + break; + } + + // Parse the command + if (i < length) { + char command = buffer[i++]; + + switch (command) { + // Move the cursor to the left + case 'D': + cursor -= (num > 0) ? num : 1; + + // Don't go too far left + if (cursor < 0) + cursor = 0; + break; + + // Move the cursor to the right + case 'C': + cursor += (num > 0) ? num : 1; + + // Don't go too far right + if (cursor >= length) + cursor = length - 1; + break; + + // Move to a certain column + case 'G': + cursor = (num > 0) ? num - 1 : 0; + + // Don't go too far right + if (cursor >= length) + cursor = length - 1; + break; + + // Ignore any other sequences + default: + break; + } + } + } + break; + + // Backspace + case '\x08': + if (cursor > 0) { + cursor--; + + // Erase the previous character + line[cursor] = ' '; + } + i++; + break; + + // Carriage Return + case '\x0d': + // Erase everything up to the cursor + memset(line, '\0', cursor); + + cursor = 0; + i++; + break; + + // Normal characters + default: + if (cursor < line_length - 1) + line[cursor++] = buffer[i]; + i++; + break; + } + } + + return 0; +} + +static ssize_t pakfire_pty_send_line(struct pakfire_pty* self, + struct pakfire_pty_stdio* stdio, const char* buffer, ssize_t length) { + char line[MAX_LINE_LENGTH] = {}; + int r; + + // Sanitize the line + r = pakfire_pty_sanitize_line(self, line, buffer, length); + if (r < 0) + return r; + + // Call the callback + r = stdio->callbacks.stdout_callback(self->ctx, + stdio->callbacks.data, line, strlen(line)); + if (r < 0) + return r; + + // We have consumed the entire line + return length; +} + /* Writes as much data as possible from the buffer */ @@ -448,9 +585,8 @@ static int pakfire_pty_drain_buffer(struct pakfire_pty* pty, int fd, struct pakf eol = stdio->buffer + stdio->buffered - 1; } - // Call the callback - bytes_written = stdio->callbacks.stdout_callback( - pty->ctx, stdio->callbacks.data, stdio->buffer, eol - stdio->buffer + 1); + // Send the line + bytes_written = pakfire_pty_send_line(pty, stdio, stdio->buffer, eol - stdio->buffer + 1); // Abort on error if (bytes_written < 0)