security: harden untrusted-input handling (issue #148)
All checks were successful
CI Code / Check spelling (pull_request) Successful in 18s
CI Code / Check coding style (pull_request) Successful in 47s
CI Code / Linux (debian) (pull_request) Successful in 9m33s
CI Code / Linux (ubuntu) (pull_request) Successful in 9m41s
CI Code / Code Coverage (pull_request) Successful in 8m3s
CI Code / Linux (arch) (pull_request) Successful in 11m0s
All checks were successful
CI Code / Check spelling (pull_request) Successful in 18s
CI Code / Check coding style (pull_request) Successful in 47s
CI Code / Linux (debian) (pull_request) Successful in 9m33s
CI Code / Linux (ubuntu) (pull_request) Successful in 9m41s
CI Code / Code Coverage (pull_request) Successful in 8m3s
CI Code / Linux (arch) (pull_request) Successful in 11m0s
T02: guard the receive-path handlers that dereferenced jid_create() without a NULL check — MUC join errors, subscribed/unsubscribed presence and, with silence.non-roster enabled, every incoming message. A stanza with a missing or malformed 'from' crashed the client. The XEP-0280 carbon path carried the same class twice: the forwarded message's 'from' was handed to xmpp_jid_bare(), which dereferences the jid it is given, and a malformed 'to' left the carbon dispatch dereferencing a NULL jid_create() result (REQ-INP-01) T11: restrict /url open and /url save to http, https and aesgcm, so a received file:, javascript: or data: URL is refused (REQ-INP-06); spawn terminal-notifier through g_spawn_async with an argv instead of building a shell command for system() (REQ-INP-07); apply the XEP-0359 disco gate to MAM result ids, as live stanza-ids already do (REQ-INP-05); replace control and bidi-reordering characters in incoming message bodies with U+FFFD before they reach the terminal, the logs and the database, keeping LRM/RLM for legitimate RTL text. That pass now runs on every display path: the OX one, where the call had been left commented out since the feature landed, and outgoing carbons, whose body is forwarded by the server (REQ-INP-08); cover JID part-length boundaries and invalid UTF-8 (REQ-INP-02) T10: replace strcpy/strcat/alloca and sprintf with g_strdup_printf and g_snprintf (REQ-MEM-03); allocate the OMEMO key buffers with g_malloc so a failed allocation cannot reach the following memcpy (REQ-MEM-04); remove the variable-length arrays and enforce -Werror=vla. Two of them were sized from remote input: the disco#info feature count and a chat message word length. The flag also caught a one-past-the-end write and a leak in the plugin autocompleter bindings (REQ-MEM-09). The OX receive path leaked every decrypted body, dropping the pointer instead of freeing it, and a failed strdup no longer costs the message body
This commit is contained in:
@@ -493,7 +493,7 @@ _inp_edited(const wint_t ch)
|
||||
}
|
||||
|
||||
// printable
|
||||
char bytes[MB_CUR_MAX + 1];
|
||||
char bytes[PROF_MB_CUR_MAX + 1]; // compile-time bound, the locale value is checked at startup
|
||||
size_t utf_len = wcrtomb(bytes, ch, &mbstate);
|
||||
if (utf_len == (size_t)-1) {
|
||||
return 0;
|
||||
|
||||
@@ -454,22 +454,10 @@ _mucwin_print_triggers(ProfWin* window, const char* const message, GList* trigge
|
||||
win_appendln_highlight(window, THEME_ROOMTRIGGER, "%s", message);
|
||||
} else {
|
||||
if (first_trigger_pos > 0) {
|
||||
char message_section[strlen(message) + 1];
|
||||
int i = 0;
|
||||
while (i < first_trigger_pos) {
|
||||
message_section[i] = message[i];
|
||||
i++;
|
||||
}
|
||||
message_section[i] = '\0';
|
||||
auto_gchar gchar* message_section = g_strndup(message, (gsize)first_trigger_pos);
|
||||
win_append_highlight(window, THEME_ROOMTRIGGER, "%s", message_section);
|
||||
}
|
||||
char trigger_section[first_trigger_len + 1];
|
||||
int i = 0;
|
||||
while (i < first_trigger_len) {
|
||||
trigger_section[i] = message[first_trigger_pos + i];
|
||||
i++;
|
||||
}
|
||||
trigger_section[i] = '\0';
|
||||
auto_gchar gchar* trigger_section = g_strndup(&message[first_trigger_pos], (gsize)first_trigger_len);
|
||||
|
||||
if (first_trigger_pos + first_trigger_len < (int)strlen(message)) {
|
||||
win_append_highlight(window, THEME_ROOMTRIGGER_TERM, "%s", trigger_section);
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
#include "log.h"
|
||||
#include "config/preferences.h"
|
||||
#include "ui/ui.h"
|
||||
@@ -122,47 +123,42 @@ _notify(const char* const message, int timeout, const char* const category)
|
||||
static void
|
||||
_notify(const char* const message, int timeout, const char* const category)
|
||||
{
|
||||
GString* notify_command = g_string_new("terminal-notifier -title \"Profanity\" -message '");
|
||||
if (message == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto_char char* escaped_single = str_replace(message, "'", "'\\''");
|
||||
|
||||
if (escaped_single[0] == '<') {
|
||||
g_string_append(notify_command, "\\<");
|
||||
g_string_append(notify_command, &escaped_single[1]);
|
||||
} else if (escaped_single[0] == '[') {
|
||||
g_string_append(notify_command, "\\[");
|
||||
g_string_append(notify_command, &escaped_single[1]);
|
||||
} else if (escaped_single[0] == '(') {
|
||||
g_string_append(notify_command, "\\(");
|
||||
g_string_append(notify_command, &escaped_single[1]);
|
||||
} else if (escaped_single[0] == '{') {
|
||||
g_string_append(notify_command, "\\{");
|
||||
g_string_append(notify_command, &escaped_single[1]);
|
||||
// terminal-notifier swallows a leading <, [, ( or { — escape it for its parser
|
||||
auto_gchar gchar* msg = NULL;
|
||||
if (message[0] == '<' || message[0] == '[' || message[0] == '(' || message[0] == '{') {
|
||||
msg = g_strdup_printf("\\%s", message);
|
||||
} else {
|
||||
g_string_append(notify_command, escaped_single);
|
||||
msg = g_strdup(message);
|
||||
}
|
||||
|
||||
g_string_append(notify_command, "'");
|
||||
const gchar* argv[16]; // headroom: an added flag must not silently overrun
|
||||
guint i = 0;
|
||||
argv[i++] = "terminal-notifier";
|
||||
argv[i++] = "-title";
|
||||
argv[i++] = "Profanity";
|
||||
argv[i++] = "-message";
|
||||
argv[i++] = msg;
|
||||
|
||||
char* term_name = getenv("TERM_PROGRAM");
|
||||
char* app_id = NULL;
|
||||
const char* term_name = getenv("TERM_PROGRAM");
|
||||
if (g_strcmp0(term_name, "Apple_Terminal") == 0) {
|
||||
app_id = "com.apple.Terminal";
|
||||
argv[i++] = "-sender";
|
||||
argv[i++] = "com.apple.Terminal";
|
||||
} else if (g_strcmp0(term_name, "iTerm.app") == 0) {
|
||||
app_id = "com.googlecode.iterm2";
|
||||
argv[i++] = "-sender";
|
||||
argv[i++] = "com.googlecode.iterm2";
|
||||
}
|
||||
argv[i] = NULL;
|
||||
|
||||
if (app_id) {
|
||||
g_string_append(notify_command, " -sender ");
|
||||
g_string_append(notify_command, app_id);
|
||||
GError* err = NULL;
|
||||
// argv-based spawn: the message is one argument, the shell never sees it
|
||||
if (!g_spawn_async(NULL, (gchar**)argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &err)) {
|
||||
log_error("Could not send desktop notification: %s", err->message);
|
||||
g_error_free(err);
|
||||
}
|
||||
|
||||
int res = system(notify_command->str);
|
||||
if (res == -1) {
|
||||
log_error("Could not send desktop notification.");
|
||||
}
|
||||
|
||||
g_string_free(notify_command, TRUE);
|
||||
}
|
||||
#else
|
||||
static void
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <curses.h>
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
#include "log.h"
|
||||
#include "config/theme.h"
|
||||
#include "config/preferences.h"
|
||||
@@ -2115,7 +2116,7 @@ _win_print_wrapped(WINDOW* win, const char* const message, int indent, int pad_i
|
||||
_win_indent(win, indent + pad_indent);
|
||||
}
|
||||
|
||||
gchar copy[wordi + 1];
|
||||
gchar copy[PROF_MB_CUR_MAX + 1]; // one UTF-8 character, not the whole word
|
||||
g_utf8_strncpy(copy, word_ch, 1);
|
||||
waddstr(win, copy);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user