1
0
mirror of https://github.com/weechat/weechat.git synced 2026-07-05 17:23:15 +02:00

Compare commits

...

32 Commits

Author SHA1 Message Date
Sébastien Helleu 24fbeaa9a8 Version 4.9.3 2026-07-05 15:40:23 +02:00
Sébastien Helleu 8119823a17 core: mute compiler warning on calls to dup() function 2026-07-05 14:26:15 +02:00
Sébastien Helleu fa29dd8e63 api: do not free dynamic string on error in function string_dyn_concat 2026-07-05 13:08:43 +02:00
Sébastien Helleu de3a771d53 core: fix possible buffer overflow in list of commands displayed by /help (issue #2330)
Fix: c.lang.security.insecure-use-strcat-fn.insecure-use-strcat-fn security vulnerability

Found by OrbisAI Security
2026-07-05 13:08:30 +02:00
orbisai0security 49a3fd7ae6 core: fix possible buffer overflow in command /color alias (issue #2330)
Fix: c.lang.security.insecure-use-strcat-fn.insecure-use-strcat-fn security vulnerability

Automated security fix generated by OrbisAI Security
2026-07-05 13:08:13 +02:00
Sébastien Helleu 3bd95f0d12 core: update ChangeLog (GHSA-wmpc-m6g9-fwj8) 2026-07-05 12:58:30 +02:00
Sébastien Helleu 57795ddb98 core: set max curl version to 8.22.0 for TLSAUTH symbols 2026-07-05 12:55:32 +02:00
Sébastien Helleu 0645731b9b ci: bump poexam to version 0.0.12 2026-07-03 08:31:46 +02:00
Sébastien Helleu 9d43e2eed7 core: fix punctuation in German translation 2026-07-03 08:30:58 +02:00
Matthew Horan acaf528628 relay/api: only decompress compressed messages
With permessage-deflate, RSV1 of the first fragment indicates whether or
not the message is compressed [1]. If RSV1 is not set then the message
should not be decompressed.

[1] https://datatracker.ietf.org/doc/html/rfc7692#section-6
2026-07-03 07:51:14 +02:00
Sébastien Helleu cfa59405cf core: set pointers to NULL after free of data when a buffer is closed (issue #2332) 2026-06-30 10:18:05 +02:00
Matthew Horan 9388c01074 doc/api: note that colors param is supported nicks endpoint 2026-06-30 10:17:20 +02:00
Sébastien Helleu 0cd736af22 relay/api: fix memory leak in resources "handshake", "input" and "completion" 2026-06-17 21:55:06 +02:00
aizu-m 703120bbfb xfer: fix out-of-bounds write in xfer_dcc_resume_hash (#2326) 2026-06-17 21:31:53 +02:00
aizu-m 12c4170fbf core: fix buffer overflow in function network_pass_socks5proxy (#2325)
bound the configured proxy username and password before they are copied into the fixed stack buffer in network_pass_socks5proxy, otherwise a login longer than the buffer (a long password or token) overruns it while building the SOCKS5 auth request.
2026-06-12 13:03:20 +02:00
Sébastien Helleu e9138c5d55 core: add CVE IDs in ChangeLog 2026-06-09 22:12:25 +02:00
Sébastien Helleu eb8c8641ea Version 4.9.3-dev 2026-06-07 09:28:03 +02:00
Sébastien Helleu bd6455d07f Version 4.9.2 2026-06-07 09:25:09 +02:00
Sébastien Helleu 8d3180fa78 tests: increase buffer size for injection of fake IRC message 2026-06-07 08:48:47 +02:00
aizu-m 519ab4e7bb relay: fix out-of-bounds read in relay_http_print_log_request (#2324) 2026-06-06 11:20:05 +02:00
Sébastien Helleu 3c36fd7412 relay: limit size of partial message received while reading an HTTP request to prevent memory exhaustion
A relay client could send data with no end-of-line (an unterminated method
or header line) and dribble its payload, making WeeChat accumulate it in the
partial message buffer that grew without limit, until all memory was
exhausted. This path is reachable before authentication during websocket
initialization with the "weechat" and "irc" protocols.

The accumulated partial message is now bounded by
RELAY_HTTP_PARTIAL_MESSAGE_MAX_LENGTH: once the limit is reached, the extra
data is ignored.
2026-06-06 09:38:55 +02:00
Sébastien Helleu b62c97dbe3 core: add links to issues in ChangeLog (#2321, #2322) 2026-06-06 07:25:19 +02:00
aizu-m f91f92b48f xfer: fix out-of-bounds read in xfer_chat_recv_cb on empty line (#2323) 2026-06-06 07:21:55 +02:00
aizu-m 30529057c8 irc: fix out-of-bounds read in DCC command with quoted filename 2026-06-04 23:20:59 +02:00
Sébastien Helleu a69f356182 tests: add tests on function xfer_file_find_filename 2026-06-04 23:20:26 +02:00
aizu-m 1438255a87 xfer: replace directory separator in remote nick by underscore in download filename 2026-06-04 23:20:08 +02:00
Sébastien Helleu d15ce789a0 api: fix infinite loop in function string_replace when the search string is empty 2026-06-03 21:17:32 +02:00
Sébastien Helleu 377b6da43d relay: limit size of received websocket frame and HTTP body to prevent memory exhaustion
A relay client could announce a huge websocket frame (or HTTP body via
"Content-Length") and dribble its payload, making WeeChat accumulate it
in a buffer that grew without limit, until all memory was exhausted. The
websocket frame path is reachable before authentication with the
"weechat" and "irc" protocols.

The announced websocket frame length and HTTP "Content-Length" are now
bounded by WEBSOCKET_FRAME_MAX_LENGTH and RELAY_HTTP_BODY_MAX_LENGTH: an
oversized websocket frame closes the connection, and an oversized body is
rejected.
2026-06-01 22:09:27 +02:00
Sébastien Helleu 8b1b06a407 irc: limit size of data received from the server to prevent memory exhaustion
A malicious or compromised IRC server could send data with no end-of-line
(or a flood of "005" messages), making WeeChat accumulate it in a buffer
that grew without limit, until all memory was exhausted.

The unterminated received message and the accumulated "005" (ISUPPORT)
data are now bounded by IRC_SERVER_RECV_MSG_MAX_LENGTH and
IRC_SERVER_ISUPPORT_MAX_LENGTH: extra data is ignored once the limit is
reached.
2026-06-01 22:08:39 +02:00
Sébastien Helleu c09c1bf2fc ci: enable ruby 3.3 module on Rocky Linux 9 2026-05-31 15:19:37 +02:00
Sébastien Helleu c83afb9a06 ci: install dnf-plugins-core on Rocky Linux 9 for dnf config-manager 2026-05-31 15:18:42 +02:00
Sébastien Helleu ed9535a43f Version 4.9.2-dev 2026-05-31 13:50:17 +02:00
37 changed files with 565 additions and 67 deletions
+4 -2
View File
@@ -131,7 +131,7 @@ jobs:
sudo apt-get update -qq
sudo apt-get --yes --no-install-recommends install ${{ env.CHECK_DEPS_UBUNTU }}
pipx install msgcheck ruff
cargo install --version 0.0.10 poexam
cargo install --version 0.0.12 poexam
- name: Check gettext files (msgcheck)
run: msgcheck po/*.po
@@ -280,8 +280,10 @@ jobs:
- name: Install dependencies
run: |
dnf install -y epel-release
dnf install -y epel-release dnf-plugins-core
dnf config-manager --set-enabled crb
# pin a working ruby stream (ruby:4.0 has broken module metadata on Rocky 9.8)
dnf module enable -y ruby:3.3
dnf install -y ${{ env.WEECHAT_DEPS_ROCKYLINUX }}
- name: Build and run tests
+2
View File
@@ -7,9 +7,11 @@ select = [
"checks",
]
ignore = [
"acronyms",
"brackets",
"double-quotes",
"double-words",
"functions",
"header",
"html-tags",
"paths",
+28 -3
View File
@@ -6,15 +6,40 @@ SPDX-License-Identifier: GPL-3.0-or-later
# WeeChat ChangeLog
## Version 4.9.3 (2026-07-05)
### Fixed
- core: fix buffer overflow in connection to SOCKS5 proxy ([#2325](https://github.com/weechat/weechat/issues/2325))
- core: fix possible buffer overflow in command /color alias ([#2330](https://github.com/weechat/weechat/issues/2330))
- core: fix possible buffer overflow in list of commands displayed by /help ([#2330](https://github.com/weechat/weechat/issues/2330))
- api: do not free dynamic string on error in function string_dyn_concat
- relay/api: fix memory leak in resources "handshake", "input" and "completion" ([GHSA-wmpc-m6g9-fwj8](https://github.com/weechat/weechat/security/advisories/GHSA-wmpc-m6g9-fwj8))
- relay: fix read of uncompressed websocket frame ([#2331](https://github.com/weechat/weechat/issues/2331))
- xfer: fix out-of-bounds write in xfer file transfer resume ([#2326](https://github.com/weechat/weechat/issues/2326))
## Version 4.9.2 (2026-06-07)
### Fixed
- api: fix infinite loop in function string_replace when the search string is empty
- irc: limit size of data received from the server to prevent memory exhaustion
- irc: fix out-of-bounds read on incoming DCC command with a quoted filename ending the message ([#2322](https://github.com/weechat/weechat/issues/2322))
- relay: limit size of received websocket frame and HTTP body to prevent memory exhaustion
- relay: limit size of partial message received while reading an HTTP request to prevent memory exhaustion
- relay: fix out-of-bounds read in dump of data ([#2324](https://github.com/weechat/weechat/issues/2324))
- xfer: replace directory separator in remote nick by underscore in download filename to prevent writing the file outside the download directory ([#2321](https://github.com/weechat/weechat/issues/2321))
- xfer: fix out-of-bounds read when receiving empty line in DCC chat ([#2323](https://github.com/weechat/weechat/issues/2323))
## Version 4.9.1 (2026-05-31)
### Fixed
- core: fix option weechat.look.color_real_white not applied when color is "white" on 16+ colors terminals ([#1742](https://github.com/weechat/weechat/issues/1742))
- irc: fix tag in message with list of names when joining a channel
- relay: limit size of decompressed websocket frame with permessage-deflate to prevent memory exhaustion ([GHSA-v2v4-45wm-5cr3](https://github.com/weechat/weechat/security/advisories/GHSA-v2v4-45wm-5cr3))
- relay: fix timing attack on password authentication ([GHSA-vhv8-g2r9-cwcc](https://github.com/weechat/weechat/security/advisories/GHSA-vhv8-g2r9-cwcc))
- api, relay: fix timing attack on TOTP validation ([GHSA-vhv8-g2r9-cwcc](https://github.com/weechat/weechat/security/advisories/GHSA-vhv8-g2r9-cwcc))
- relay: limit size of decompressed websocket frame with permessage-deflate to prevent memory exhaustion ([GHSA-v2v4-45wm-5cr3](https://github.com/weechat/weechat/security/advisories/GHSA-v2v4-45wm-5cr3), [CVE-2026-53524](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2026-53524))
- relay: fix timing attack on password authentication ([GHSA-vhv8-g2r9-cwcc](https://github.com/weechat/weechat/security/advisories/GHSA-vhv8-g2r9-cwcc), [CVE-2026-53525](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2026-53525))
- api, relay: fix timing attack on TOTP validation ([GHSA-vhv8-g2r9-cwcc](https://github.com/weechat/weechat/security/advisories/GHSA-vhv8-g2r9-cwcc), [CVE-2026-53525](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2026-53525))
## Version 4.9.0 (2026-03-29)
+2
View File
@@ -3474,6 +3474,8 @@ Concatenate a string to a dynamic string.
The pointer _*string_ can change if the string is reallocated (if there is
not enough space to concatenate the string).
In case of error, the dynamic string is left unchanged.
Prototype:
[source,c]
+7
View File
@@ -1028,6 +1028,13 @@ Path parameters:
confused with the buffer number, which is different)
* `buffer_name` (string, **required**): buffer name
Query parameters:
* `colors` (string, optional, default: `ansi`): how to return strings with color codes:
** `ansi`: return ANSI color codes
** `weechat`: return WeeChat internal color codes
** `strip`: strip colors
Request example: get nicks of a buffer:
[source,shell]
+2
View File
@@ -3532,6 +3532,8 @@ Concaténer une chaîne dans une chaîne dynamique.
Le pointeur _*string_ peut changer si la chaîne est réallouée (s'il n'y a pas
assez de place pour concaténer la chaîne).
En cas d'erreur, la chaîne dynamique reste inchangée.
Prototype :
[source,c]
+8
View File
@@ -1040,6 +1040,14 @@ Paramètres de chemin :
confondre avec le numéro du tampon, qui est différent)
* `buffer_name` (chaîne, **obligatoire**) : nom du tampon
Paramètres de requête :
* `colors` (chaîne, facultatif, par défaut : `ansi`) : comment les chaînes avec
des couleurs sont retournées :
** `ansi` : retourner les codes couleur ANSI
** `weechat` : retourner les codes couleur internes WeeChat
** `strip` : supprimer les couleurs
Exemple de requête : obtenir les pseudos d'un tampon :
[source,shell]
+3
View File
@@ -3635,6 +3635,9 @@ Concatenate a string to a dynamic string.
The pointer _*string_ can change if the string is reallocated (if there is
not enough space to concatenate the string).
// TRANSLATION MISSING
In case of error, the dynamic string is left unchanged.
Prototipo:
[source,c]
+3
View File
@@ -3586,6 +3586,9 @@ _WeeChat バージョン 1.8 以上で利用可, updated in 3.0_
文字列が再確保された場合 (文字列を連結するのに十分なサイズが確保されていなかった場合)
にはポインタ _*string_ が変わる可能性があります。
// TRANSLATION MISSING
In case of error, the dynamic string is left unchanged.
プロトタイプ:
[source,c]
+3
View File
@@ -3356,6 +3356,9 @@ _WeeChat ≥ 1.8, ажурирано у верзији 3.0._
Показивач на стринг _*string_ може да се промени ако се стринг реалоцира (у случају да нема довољно простора за надовезивање стринга).
// TRANSLATION MISSING
In case of error, the dynamic string is left unchanged.
Прототип:
[source,c]
+8 -1
View File
@@ -1024,12 +1024,19 @@ GET /api/buffers/{id_бафера}/nicks
GET /api/buffers/{име_бафера}/nicks
----
Параметри упита:
Параметри путање:
* `id_бафера` (цео број, **обавезно**): јединствени идентификатор бафера (не треба
да се помеша са бројем бафера, то је нешто друго)
* `име_бафера` (стринг, **обавезно**): име бафера
Параметри упита:
* `colors` (стринг, није обавезно, подразумевано: `ansi`): како се враћају стрингови са кодовима боје:
** `ansi`: враћају се ANSI кодови боје
** `weechat`: враћају се WeeChat интерни кодови боје
** `strip`: уклањају се боје
Пример захтева: врати надимке бафера:
[source,shell]
+2 -2
View File
@@ -29,7 +29,7 @@ msgstr ""
"Project-Id-Version: WeeChat\n"
"Report-Msgid-Bugs-To: flashcode@flashtux.org\n"
"POT-Creation-Date: 2026-03-21 17:24+0100\n"
"PO-Revision-Date: 2026-03-21 17:24+0100\n"
"PO-Revision-Date: 2026-07-03 08:28+0200\n"
"Last-Translator: Nils Görs <weechatter@arcor.de>\n"
"Language-Team: German <kde-i18n-de@kde.org>\n"
"Language: de_DE\n"
@@ -17330,7 +17330,7 @@ msgstr ""
"Um Tastenkurzbefehle im Skript-Buffer direkt nutzen zu können (zum Beispiel: "
"alt+i = installieren, alt+r = entfernen, ...), muss diese Einstellung "
"aktiviert werden. Andernfalls können Aktionen nur über die Eingabezeile "
"durchgeführt werden: i,r..."
"durchgeführt werden: i,r, ..."
msgid "color for status \"autoloaded\" (\"a\")"
msgstr "Farbe in der der Status \"autoloaded\" (\"a\") dargestellt werden soll"
+19 -22
View File
@@ -1720,17 +1720,12 @@ COMMAND_CALLBACK(color)
else
str_alias = argv[i];
}
str_color[0] = '\0';
if (str_alias)
{
strcat (str_color, ";");
strcat (str_color, str_alias);
}
if (str_rgb)
{
strcat (str_color, ";");
strcat (str_color, str_rgb);
}
snprintf (str_color, sizeof (str_color),
"%s%s%s%s",
(str_alias) ? ";" : "",
(str_alias) ? str_alias : "",
(str_rgb) ? ";" : "",
(str_rgb) ? str_rgb : "");
/* add color alias */
snprintf (str_command, sizeof (str_command),
@@ -2980,7 +2975,7 @@ command_help_list_plugin_commands (struct t_weechat_plugin *plugin,
struct t_gui_buffer *ptr_buffer;
int command_found, length, max_length, list_size;
int cols, lines, col, line, index;
char str_format[64], str_command[256], str_line[2048];
char str_format[64], str_command[256], **str_line;
if (verbose)
{
@@ -3083,27 +3078,29 @@ command_help_list_plugin_commands (struct t_weechat_plugin *plugin,
}
/* display lines with commands, in columns */
for (line = 0; line < lines; line++)
str_line = string_dyn_alloc (256);
if (str_line)
{
str_line[0] = '\0';
for (col = 0; col < cols; col++)
for (line = 0; line < lines; line++)
{
index = (col * lines) + line;
if (index < list_size)
string_dyn_copy (str_line, NULL);
for (col = 0; col < cols; col++)
{
item = weelist_get (list, index);
if (item)
index = (col * lines) + line;
if (index < list_size)
{
if (strlen (str_line) + strlen (weelist_string (item)) + 1 < (int)sizeof (str_line))
item = weelist_get (list, index);
if (item)
{
snprintf (str_command, sizeof (str_command),
str_format, weelist_string (item));
strcat (str_line, str_command);
string_dyn_concat (str_line, str_command, -1);
}
}
}
gui_chat_printf (NULL, "%s", *str_line);
}
gui_chat_printf (NULL, "%s", str_line);
string_dyn_free (str_line, 1);
}
}
+19 -1
View File
@@ -581,7 +581,13 @@ network_pass_socks5proxy (struct t_proxy *proxy, int sock, const char *address,
int port)
{
struct t_network_socks5 socks5;
unsigned char buffer[288];
/*
* buffer must be large enough for the username/password authentication
* request, which is the longest message sent/received here; according to
* RFC 1929 it is: version (1) + username length (1) + username (max 255)
* + password length (1) + password (max 255)
*/
unsigned char buffer[2 + 255 + 1 + 255];
int username_len, password_len, addr_len, addr_buffer_len;
unsigned char *addr_buffer;
char *username, *password;
@@ -630,6 +636,18 @@ network_pass_socks5proxy (struct t_proxy *proxy, int sock, const char *address,
username_len = strlen (username);
password_len = strlen (password);
/*
* username and password length are each stored on a single byte
* (RFC 1929), so they cannot exceed 255 bytes: reject longer values,
* otherwise the memcpy calls below would overflow the buffer
*/
if ((username_len > 255) || (password_len > 255))
{
free (username);
free (password);
return 0;
}
/* make username/password buffer */
buffer[0] = 1;
buffer[1] = (unsigned char) username_len;
+5 -4
View File
@@ -1965,6 +1965,9 @@ string_replace (const char *string, const char *search, const char *replace)
if (!string || !search || !replace)
return NULL;
if (!search[0])
return strdup (string);
length1 = strlen (search);
length2 = strlen (replace);
@@ -4765,6 +4768,8 @@ string_dyn_copy (char **string, const char *new_string)
* if the string had to be extended, or the same pointer if there was enough
* size to concatenate the new string.
*
* In case of error, the dynamic string is left unchanged.
*
* Return:
* 1: OK
* 0: error
@@ -4800,11 +4805,7 @@ string_dyn_concat (char **string, const char *add, int bytes)
new_size_alloc = new_size;
string_realloc = realloc (ptr_string_dyn->string, new_size_alloc);
if (!string_realloc)
{
free (ptr_string_dyn->string);
free (ptr_string_dyn);
return 0;
}
ptr_string_dyn->string = string_realloc;
ptr_string_dyn->size_alloc = new_size_alloc;
}
+6
View File
@@ -143,8 +143,10 @@ struct t_url_constant url_auth[] =
struct t_url_constant url_authtype[] =
{
#if LIBCURL_VERSION_NUM < 0x081600 /* < 8.22.0 */
URL_DEF_CONST(_TLSAUTH, NONE),
URL_DEF_CONST(_TLSAUTH, SRP),
#endif
{ NULL, 0 },
};
@@ -409,9 +411,11 @@ struct t_url_option url_options[] =
URL_DEF_OPTION(PASSWORD, STRING, NULL),
URL_DEF_OPTION(PROXYUSERNAME, STRING, NULL),
URL_DEF_OPTION(PROXYPASSWORD, STRING, NULL),
#if LIBCURL_VERSION_NUM < 0x081600 /* < 8.22.0 */
URL_DEF_OPTION(TLSAUTH_TYPE, MASK, url_authtype),
URL_DEF_OPTION(TLSAUTH_USERNAME, STRING, NULL),
URL_DEF_OPTION(TLSAUTH_PASSWORD, STRING, NULL),
#endif
URL_DEF_OPTION(SASL_AUTHZID, STRING, NULL),
URL_DEF_OPTION(SASL_IR, LONG, NULL),
@@ -626,9 +630,11 @@ struct t_url_option url_options[] =
URL_DEF_OPTION(PROXY_SSL_OPTIONS, LONG, url_ssl_options),
URL_DEF_OPTION(PROXY_SSL_VERIFYHOST, LONG, NULL),
URL_DEF_OPTION(PROXY_SSL_VERIFYPEER, LONG, NULL),
#if LIBCURL_VERSION_NUM < 0x081600 /* < 8.22.0 */
URL_DEF_OPTION(PROXY_TLSAUTH_PASSWORD, STRING, NULL),
URL_DEF_OPTION(PROXY_TLSAUTH_TYPE, STRING, NULL),
URL_DEF_OPTION(PROXY_TLSAUTH_USERNAME, STRING, NULL),
#endif
URL_DEF_OPTION(TLS13_CIPHERS, LIST, NULL),
URL_DEF_OPTION(PROXY_TLS13_CIPHERS, LIST, NULL),
#if LIBCURL_VERSION_NUM >= 0x074700 /* 7.71.0 */
+4 -3
View File
@@ -47,7 +47,7 @@ void
daemonize (void)
{
pid_t pid;
int fd, i;
int fd, i, rc;
printf ("%s ", _("Running WeeChat in background..."));
@@ -77,8 +77,9 @@ daemonize (void)
close (i);
}
fd = open ("/dev/null", O_RDWR);
(void) dup (fd);
(void) dup (fd);
rc = dup (fd);
rc = dup (fd);
(void) rc;
}
/*
+31
View File
@@ -3804,6 +3804,7 @@ gui_buffer_close (struct t_gui_buffer *buffer)
gui_hotlist_remove_buffer (buffer, 1);
free (buffer->hotlist_removed);
buffer->hotlist_removed = NULL;
if (gui_hotlist_initial_buffer == buffer)
gui_hotlist_initial_buffer = NULL;
@@ -3822,55 +3823,85 @@ gui_buffer_close (struct t_gui_buffer *buffer)
/* free all lines */
gui_line_free_all (buffer);
free (buffer->own_lines);
buffer->own_lines = NULL;
free (buffer->mixed_lines);
buffer->mixed_lines = NULL;
/* free some data */
gui_buffer_undo_free_all (buffer);
gui_history_buffer_free (buffer);
gui_completion_free (buffer->completion);
buffer->completion = NULL;
gui_nicklist_remove_all (buffer);
gui_nicklist_remove_group (buffer, buffer->nicklist_root);
buffer->nicklist_root = NULL;
hashtable_free (buffer->hotlist_max_level_nicks);
buffer->hotlist_max_level_nicks = NULL;
gui_key_free_all (-1, &buffer->keys, &buffer->last_key,
&buffer->keys_count, 0);
gui_buffer_local_var_remove_all (buffer);
hashtable_free (buffer->local_variables);
buffer->local_variables = NULL;
free (buffer->plugin_name_for_upgrade);
buffer->plugin_name_for_upgrade = NULL;
free (buffer->name);
buffer->name = NULL;
free (buffer->full_name);
buffer->full_name = NULL;
free (buffer->old_full_name);
buffer->old_full_name = NULL;
free (buffer->short_name);
buffer->short_name = NULL;
free (buffer->title);
buffer->title = NULL;
free (buffer->modes);
buffer->modes = NULL;
free (buffer->input_prompt);
buffer->input_prompt = NULL;
free (buffer->input_buffer);
buffer->input_buffer = NULL;
free (buffer->input_undo_snap);
buffer->input_undo_snap = NULL;
free (buffer->text_search_input);
buffer->text_search_input = NULL;
if (buffer->text_search_regex_compiled)
{
regfree (buffer->text_search_regex_compiled);
free (buffer->text_search_regex_compiled);
buffer->text_search_regex_compiled = NULL;
}
free (buffer->highlight_words);
buffer->highlight_words = NULL;
free (buffer->highlight_disable_regex);
buffer->highlight_disable_regex = NULL;
if (buffer->highlight_disable_regex_compiled)
{
regfree (buffer->highlight_disable_regex_compiled);
free (buffer->highlight_disable_regex_compiled);
buffer->highlight_disable_regex_compiled = NULL;
}
free (buffer->highlight_regex);
buffer->highlight_regex = NULL;
if (buffer->highlight_regex_compiled)
{
regfree (buffer->highlight_regex_compiled);
free (buffer->highlight_regex_compiled);
buffer->highlight_regex_compiled = NULL;
}
free (buffer->highlight_tags_restrict);
buffer->highlight_tags_restrict = NULL;
string_free_split_tags (buffer->highlight_tags_restrict_array);
buffer->highlight_tags_restrict_array = NULL;
free (buffer->highlight_tags);
buffer->highlight_tags = NULL;
string_free_split_tags (buffer->highlight_tags_array);
buffer->highlight_tags_array = NULL;
free (buffer->input_callback_data);
buffer->input_callback_data = NULL;
free (buffer->close_callback_data);
buffer->close_callback_data = NULL;
free (buffer->nickcmp_callback_data);
buffer->nickcmp_callback_data = NULL;
/* remove buffer from buffers list */
if (buffer->prev_buffer)
+3 -3
View File
@@ -857,7 +857,7 @@ irc_ctcp_recv_dcc (struct t_irc_protocol_ctxt *ctxt, const char *arguments)
* double-quote
*/
pos = strrchr (pos_file, '"');
if (!pos || (pos == pos_file))
if (!pos || (pos == pos_file) || !pos[1])
{
weechat_printf (
ctxt->server->buffer,
@@ -1032,7 +1032,7 @@ irc_ctcp_recv_dcc (struct t_irc_protocol_ctxt *ctxt, const char *arguments)
* double-quote
*/
pos = strrchr (pos_file, '"');
if (!pos || (pos == pos_file))
if (!pos || (pos == pos_file) || !pos[1])
{
weechat_printf (
ctxt->server->buffer,
@@ -1176,7 +1176,7 @@ irc_ctcp_recv_dcc (struct t_irc_protocol_ctxt *ctxt, const char *arguments)
* double-quote
*/
pos = strrchr (pos_file, '"');
if (!pos || (pos == pos_file))
if (!pos || (pos == pos_file) || !pos[1])
{
weechat_printf (
ctxt->server->buffer,
+18 -9
View File
@@ -4164,16 +4164,25 @@ IRC_PROTOCOL_CALLBACK(005)
if (ctxt->server->isupport)
{
length_isupport = strlen (ctxt->server->isupport);
isupport2 = realloc (ctxt->server->isupport,
length_isupport + /* existing */
1 + /* space */
length + /* new */
1);
if (isupport2)
/*
* limit the size of the accumulated ISUPPORT data: once the
* maximum is reached, ignore the extra data (protection against a
* server flooding "005" messages, which would consume all the
* memory)
*/
if (length_isupport + 1 + length < IRC_SERVER_ISUPPORT_MAX_LENGTH)
{
ctxt->server->isupport = isupport2;
strcat (ctxt->server->isupport, " ");
strcat (ctxt->server->isupport, str_info);
isupport2 = realloc (ctxt->server->isupport,
length_isupport + /* existing */
1 + /* space */
length + /* new */
1);
if (isupport2)
{
ctxt->server->isupport = isupport2;
strcat (ctxt->server->isupport, " ");
strcat (ctxt->server->isupport, str_info);
}
}
}
else
+8
View File
@@ -3409,6 +3409,14 @@ irc_server_msgq_add_unterminated (struct t_irc_server *server,
if (server->unterminated_message)
{
/*
* limit the size of the unterminated message: once the maximum is
* reached, ignore the extra data (protection against a server sending
* a very long line without end-of-line, which would consume all the
* memory)
*/
if (strlen (server->unterminated_message) >= IRC_SERVER_RECV_MSG_MAX_LENGTH)
return;
unterminated_message2 =
realloc (server->unterminated_message,
(strlen (server->unterminated_message) +
+9
View File
@@ -144,6 +144,15 @@ enum t_irc_server_option
#define IRC_SERVER_MULTILINE_DEFAULT_MAX_BYTES 4096
#define IRC_SERVER_MULTILINE_DEFAULT_MAX_LINES 24
/*
* maximum length of an unterminated message (a received line without
* end-of-line) and of the accumulated "005" (ISUPPORT) data; these limits
* protect against a server sending a huge amount of data without end-of-line
* (or a flood of "005" messages), which would consume all the memory
*/
#define IRC_SERVER_RECV_MSG_MAX_LENGTH (64 * 1024)
#define IRC_SERVER_ISUPPORT_MAX_LENGTH (64 * 1024)
/* casemapping (string comparisons for nicks/channels) */
enum t_irc_server_casemapping
{
+15 -2
View File
@@ -402,7 +402,10 @@ RELAY_API_PROTOCOL_CALLBACK(handshake)
if (json_body)
{
if (!cJSON_IsObject (json_body))
{
cJSON_Delete (json_body);
return RELAY_API_PROTOCOL_RC_BAD_REQUEST;
}
json_algos = cJSON_GetObjectItem (json_body, "password_hash_algo");
if (json_algos)
{
@@ -781,8 +784,13 @@ RELAY_API_PROTOCOL_CALLBACK(input)
char str_delay[32];
json_body = cJSON_Parse (client->http_req->body);
if (!json_body || !cJSON_IsObject (json_body))
if (!json_body)
return RELAY_API_PROTOCOL_RC_BAD_REQUEST;
if (!cJSON_IsObject (json_body))
{
cJSON_Delete (json_body);
return RELAY_API_PROTOCOL_RC_BAD_REQUEST;
}
/* get buffer either by name or by id */
ptr_buffer = NULL;
@@ -908,8 +916,13 @@ RELAY_API_PROTOCOL_CALLBACK(completion)
struct t_gui_buffer *ptr_buffer;
json_body = cJSON_Parse (client->http_req->body);
if (!json_body || !cJSON_IsObject(json_body))
if (!json_body)
return RELAY_API_PROTOCOL_RC_BAD_REQUEST;
if (!cJSON_IsObject(json_body))
{
cJSON_Delete (json_body);
return RELAY_API_PROTOCOL_RC_BAD_REQUEST;
}
/* get buffer either by name or by id */
ptr_buffer = NULL;
+22 -1
View File
@@ -513,6 +513,19 @@ relay_http_add_to_body (struct t_relay_http_request *request,
if (!partial_message || !*partial_message)
return;
/*
* reject the body if its announced length is too big: this prevents a
* client from forcing an unbounded allocation by announcing a huge
* "Content-Length"
*/
if (request->content_length > RELAY_HTTP_BODY_MAX_LENGTH)
{
free (*partial_message);
*partial_message = NULL;
request->status = RELAY_HTTP_END;
return;
}
num_bytes_missing = request->content_length
- request->body_size;
if (num_bytes_missing <= 0)
@@ -993,6 +1006,14 @@ relay_http_recv (struct t_relay_client *client, const char *data, int size)
if (client->partial_message)
{
/*
* limit the size of the partial message: once the maximum is reached,
* ignore the extra data (protection against a client sending a huge
* amount of data without any end-of-line and dribbling it, which would
* consume all the memory)
*/
if (strlen (client->partial_message) >= RELAY_HTTP_PARTIAL_MESSAGE_MAX_LENGTH)
return;
new_partial = realloc (client->partial_message,
strlen (client->partial_message) +
strlen (data) + 1);
@@ -1695,7 +1716,7 @@ relay_http_print_log_request (struct t_relay_http_request *request)
weechat_log_printf (" path_items. . . . . . . : %p", request->path_items);
if (request->path_items)
{
for (i = 0; request->path_items[0]; i++)
for (i = 0; request->path_items[i]; i++)
{
weechat_log_printf (" '%s'", request->path_items[i]);
}
+16
View File
@@ -57,6 +57,22 @@ enum t_relay_client_http_status
#define RELAY_HTTP_ERROR_METHOD_NOT_ALLOWED "Method Not Allowed"
#define RELAY_HTTP_ERROR_OUT_OF_MEMORY "Out of memory"
/*
* maximum length of an HTTP request body: used as an upper bound on the
* "Content-Length" accepted from a client, to prevent a client from forcing
* an unbounded allocation by announcing a huge body
*/
#define RELAY_HTTP_BODY_MAX_LENGTH (8 * 1024 * 1024)
/*
* maximum length of the partial message accumulated while reading an HTTP
* request: once this limit is reached, the extra data is ignored; this
* protects against a client sending a huge amount of data without any
* end-of-line (an unterminated method or header line), which would consume
* all the memory
*/
#define RELAY_HTTP_PARTIAL_MESSAGE_MAX_LENGTH (8 * 1024 * 1024)
struct t_relay_http_request
{
enum t_relay_client_http_status status; /* HTTP status */
+14 -3
View File
@@ -653,7 +653,7 @@ relay_websocket_decode_frame (const unsigned char *buffer,
size_t size_decompressed;
char *payload_decompressed;
struct t_relay_websocket_frame *frames2, *ptr_frame;
int size, masked_frame, mask[4];
int size, compressed, masked_frame, mask[4];
if (!buffer || !frames || !num_frames)
return 0;
@@ -674,6 +674,9 @@ relay_websocket_decode_frame (const unsigned char *buffer,
opcode = buffer[index_buffer] & 15;
/* RSV1 indicates whether this message is compressed */
compressed = (buffer[index_buffer] & 64) ? 1 : 0;
/* check if frame is masked */
masked_frame = (buffer[index_buffer + 1] & 128) ? 1 : 0;
@@ -703,6 +706,14 @@ relay_websocket_decode_frame (const unsigned char *buffer,
index_buffer += length_frame_size;
}
/*
* reject the frame if its announced length is too big: this prevents
* a client from forcing an unbounded allocation (and unbounded
* accumulation of partial frames) by announcing a huge frame
*/
if (length_frame > WEBSOCKET_FRAME_MAX_LENGTH)
return 0;
if (masked_frame)
{
/* read mask (4 bytes) */
@@ -772,9 +783,9 @@ relay_websocket_decode_frame (const unsigned char *buffer,
/*
* decompress data if frame is not empty and if "permessage-deflate"
* is enabled
* is enabled and the message is compressed
*/
if ((length_frame > 0) && ws_deflate && ws_deflate->enabled)
if ((length_frame > 0) && ws_deflate && ws_deflate->enabled && compressed)
{
if (!ws_deflate->strm_inflate)
{
+8
View File
@@ -41,6 +41,14 @@
#define WEBSOCKET_SUB_PROTOCOL_API_WEECHAT "api.weechat"
/*
* maximum length of a websocket frame received from a client (or a remote
* WeeChat): used as an upper bound on the announced frame payload length, to
* prevent a client from forcing an unbounded allocation by announcing a huge
* frame and dribbling its payload
*/
#define WEBSOCKET_FRAME_MAX_LENGTH (8 * 1024 * 1024)
/*
* maximum size of a decompressed websocket frame (with "permessage-deflate"):
* used as an upper bound when inflating, to prevent a small compressed frame
+1 -1
View File
@@ -162,7 +162,7 @@ xfer_chat_recv_cb (const void *pointer, void *data, int fd)
{
ctcp_action = 0;
length = strlen (ptr_buf);
if (ptr_buf[length - 1] == '\r')
if ((length > 0) && (ptr_buf[length - 1] == '\r'))
{
ptr_buf[length - 1] = '\0';
length--;
+2 -2
View File
@@ -242,8 +242,8 @@ int
xfer_dcc_resume_hash (struct t_xfer *xfer)
{
char *buf;
unsigned long long total_read;
ssize_t length_buf, to_read, num_read;
unsigned long long total_read, length_buf, to_read;
ssize_t num_read;
int ret, fd;
total_read = 0;
+11 -3
View File
@@ -251,7 +251,7 @@ xfer_file_find_suffix (struct t_xfer *xfer)
void
xfer_file_find_filename (struct t_xfer *xfer)
{
char *dir_separator, *path;
char *dir_separator, *path, *nick;
struct t_hashtable *options;
if (!XFER_IS_FILE(xfer->type))
@@ -287,12 +287,20 @@ xfer_file_find_filename (struct t_xfer *xfer)
{
strcat (xfer->local_filename, dir_separator);
}
free (dir_separator);
if (weechat_config_boolean (xfer_config_file_use_nick_in_filename))
{
strcat (xfer->local_filename, xfer->remote_nick);
/*
* the remote nick comes from the server and can contain a directory
* separator: replace it so the nick cannot make the file be written
* outside the download directory
*/
nick = (dir_separator) ?
weechat_string_replace (xfer->remote_nick, dir_separator, "_") : NULL;
strcat (xfer->local_filename, (nick) ? nick : xfer->remote_nick);
free (nick);
strcat (xfer->local_filename, ".");
}
free (dir_separator);
strcat (xfer->local_filename, xfer->filename);
free (path);
+2
View File
@@ -1454,6 +1454,8 @@ TEST(CoreString, Replace)
WEE_TEST_STR(NULL, string_replace ("string", NULL, "replace"));
WEE_TEST_STR(NULL, string_replace (NULL, "search", "replace"));
WEE_TEST_STR("test abc def", string_replace("test abc def", "", "xxx"));
WEE_TEST_STR("test abc def", string_replace("test abc def", "xyz", "xxx"));
WEE_TEST_STR("test xxx def", string_replace("test abc def", "abc", "xxx"));
WEE_TEST_STR("xxx test xxx def xxx",
+39 -1
View File
@@ -305,7 +305,7 @@ TEST_GROUP(IrcProtocolWithServer)
void server_recv (const char *command)
{
char str_command[4096];
char str_command[8192];
record_start ();
arraylist_clear (sent_messages);
@@ -3866,6 +3866,44 @@ TEST(IrcProtocolWithServer, 005_full)
STRCMP_EQUAL(IRC_MSG_005 " " IRC_MSG_005, ptr_server->isupport);
}
/*
* Test functions:
* irc_protocol_cb_005 (accumulated ISUPPORT is bounded)
*/
TEST(IrcProtocolWithServer, 005_limit)
{
char str_msg[4096], str_value[3500];
size_t length1, length2;
int i;
SRV_INIT;
memset (str_value, 'X', sizeof (str_value) - 1);
str_value[sizeof (str_value) - 1] = '\0';
snprintf (str_msg, sizeof (str_msg),
":server 005 alice TEST=%s :are supported", str_value);
/* flood the server with "005" messages */
for (i = 0; i < 100; i++)
{
server_recv (str_msg);
}
CHECK(ptr_server->isupport);
length1 = strlen (ptr_server->isupport);
/* the accumulated ISUPPORT data must be bounded */
CHECK(length1 <= IRC_SERVER_ISUPPORT_MAX_LENGTH + sizeof (str_value));
/* receiving more "005" messages must not grow it any further */
for (i = 0; i < 100; i++)
{
server_recv (str_msg);
}
length2 = strlen (ptr_server->isupport);
LONGS_EQUAL(length1, length2);
}
/*
* Test functions:
* irc_protocol_cb_005 (infos from server, multiple messages)
@@ -65,6 +65,49 @@ TEST(IrcServer, Valid)
irc_server_free (server);
}
/*
* Test functions:
* irc_server_msgq_add_unterminated (via irc_server_msgq_add_buffer)
*
* Check that data received without any end-of-line does not grow the
* unterminated message buffer without limit.
*/
TEST(IrcServer, MsgqAddBufferLimit)
{
struct t_irc_server *server;
char chunk[4097];
int i;
size_t length1, length2;
server = irc_server_alloc ("server_msgq");
CHECK(server);
memset (chunk, 'a', sizeof (chunk) - 1);
chunk[sizeof (chunk) - 1] = '\0';
/* feed a lot of data with no end-of-line */
for (i = 0; i < 100; i++)
{
irc_server_msgq_add_buffer (server, chunk);
}
CHECK(server->unterminated_message);
length1 = strlen (server->unterminated_message);
/* the buffer must be bounded (not ~400 KB) */
CHECK(length1 <= IRC_SERVER_RECV_MSG_MAX_LENGTH + sizeof (chunk));
/* feeding more data must not grow the buffer any further */
for (i = 0; i < 100; i++)
{
irc_server_msgq_add_buffer (server, chunk);
}
length2 = strlen (server->unterminated_message);
LONGS_EQUAL(length1, length2);
irc_server_free (server);
}
/*
* Test functions:
* irc_server_search
@@ -41,6 +41,7 @@ extern "C"
#include "src/plugins/relay/relay-client.h"
#include "src/plugins/relay/relay-config.h"
#include "src/plugins/relay/relay-http.h"
#include "src/plugins/relay/relay-server.h"
#include "src/plugins/relay/relay-websocket.h"
#include "src/plugins/weechat-plugin.h"
@@ -161,6 +162,35 @@ TEST(RelayHttp, RequestAllocReinitFree)
relay_http_request_free (request);
}
/*
* Test functions:
* relay_http_add_to_body (body too large is rejected)
*/
TEST(RelayHttp, AddToBodyLimit)
{
struct t_relay_http_request *request;
char *partial;
request = relay_http_request_alloc ();
CHECK(request);
/* announce a body larger than the maximum allowed */
request->status = RELAY_HTTP_BODY;
request->content_length = RELAY_HTTP_BODY_MAX_LENGTH + 1;
partial = strdup ("some body data");
relay_http_add_to_body (request, &partial);
/* the body must be rejected: nothing allocated, request ended */
POINTERS_EQUAL(NULL, request->body);
LONGS_EQUAL(0, request->body_size);
POINTERS_EQUAL(NULL, partial);
LONGS_EQUAL(RELAY_HTTP_END, request->status);
relay_http_request_free (request);
}
/*
* Test functions:
* relay_http_url_decode
@@ -992,6 +1022,69 @@ TEST(RelayHttp, Recv)
/* TODO: write tests */
}
/*
* Test functions:
* relay_http_recv (partial message accumulated is bounded)
*
* Check that data received without any end-of-line does not grow the partial
* message buffer without limit.
*/
TEST(RelayHttp, RecvLimit)
{
struct t_relay_server *server;
struct t_relay_client *client;
char *chunk;
int chunk_size, i;
size_t length1, length2;
/* disable auto-open of relay buffer (it would pollute other tests) */
config_file_option_set (relay_config_look_auto_open_buffer, "off", 1);
server = relay_server_new ("weechat", RELAY_PROTOCOL_WEECHAT, NULL,
9000,
NULL, /* path */
1, /* ipv4 */
0, /* ipv6 */
0, /* tls */
0); /* unix_socket */
CHECK(server);
client = relay_client_new (-1, "test", server);
CHECK(client);
chunk_size = 1024 * 1024;
chunk = (char *)malloc (chunk_size + 1);
CHECK(chunk);
memset (chunk, 'a', chunk_size);
chunk[chunk_size] = '\0';
/* feed more than the maximum, with no end-of-line (16 MB) */
for (i = 0; i < 16; i++)
{
relay_http_recv (client, chunk, chunk_size);
}
CHECK(client->partial_message);
length1 = strlen (client->partial_message);
/* the partial message must be bounded (not ~16 MB) */
CHECK(length1 <= RELAY_HTTP_PARTIAL_MESSAGE_MAX_LENGTH + (size_t)chunk_size);
/* feeding more data must not grow it any further */
for (i = 0; i < 16; i++)
{
relay_http_recv (client, chunk, chunk_size);
}
length2 = strlen (client->partial_message);
LONGS_EQUAL(length1, length2);
free (chunk);
relay_client_free (client);
relay_server_free (server);
/* restore auto-open of relay buffer */
config_file_option_reset (relay_config_look_auto_open_buffer, 1);
}
/*
* Test functions:
* relay_http_compress
@@ -510,7 +510,44 @@ TEST(RelayWebsocket, Inflate)
TEST(RelayWebsocket, DecodeFrame)
{
/* TODO: write tests */
struct t_relay_websocket_frame *frames;
char *partial_ws_frame;
int num_frames, partial_ws_frame_size;
/* small unmasked binary frame with payload "hello" */
unsigned char frame_ok[7] = { 0x82, 0x05, 'h', 'e', 'l', 'l', 'o' };
/* masked frame announcing a 1 GB payload (64-bit length field) */
unsigned char frame_too_big[10] = {
0x82, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
};
/* a valid small frame is decoded */
frames = NULL;
num_frames = 0;
partial_ws_frame = NULL;
partial_ws_frame_size = 0;
LONGS_EQUAL(1, relay_websocket_decode_frame (
frame_ok, sizeof (frame_ok), 0, NULL,
&frames, &num_frames, &partial_ws_frame,
&partial_ws_frame_size));
LONGS_EQUAL(1, num_frames);
CHECK(frames);
LONGS_EQUAL(5, frames[0].payload_size);
MEMCMP_EQUAL("hello", frames[0].payload, 5);
free (frames[0].payload);
free (frames);
free (partial_ws_frame);
/* a frame announcing an oversized payload is rejected (return 0) */
frames = NULL;
num_frames = 0;
partial_ws_frame = NULL;
partial_ws_frame_size = 0;
LONGS_EQUAL(0, relay_websocket_decode_frame (
frame_too_big, sizeof (frame_too_big), 1, NULL,
&frames, &num_frames, &partial_ws_frame,
&partial_ws_frame_size));
free (frames);
free (partial_ws_frame);
}
/*
+65 -1
View File
@@ -25,6 +25,11 @@
extern "C"
{
#include <stdlib.h>
#include <string.h>
#include "src/core/core-config-file.h"
#include "src/plugins/xfer/xfer.h"
#include "src/plugins/xfer/xfer-config.h"
#include "src/plugins/xfer/xfer-file.h"
}
@@ -32,6 +37,42 @@ TEST_GROUP(XferFile)
{
};
/*
* Build a "file recv" xfer with the given remote nick (and a fixed filename),
* call xfer_file_find_filename and return a copy of the basename of the local
* filename (the part after the last directory separator).
*
* Note: result must be freed after use.
*/
static char *
test_find_filename_basename (const char *remote_nick)
{
struct t_xfer xfer;
char *pos, *result;
memset (&xfer, 0, sizeof (xfer));
xfer.type = XFER_TYPE_FILE_RECV_ACTIVE;
xfer.remote_nick = strdup (remote_nick);
xfer.filename = strdup ("test.txt");
xfer_file_find_filename (&xfer);
result = NULL;
if (xfer.local_filename)
{
pos = strrchr (xfer.local_filename, DIR_SEPARATOR_CHAR);
result = strdup ((pos) ? pos + 1 : xfer.local_filename);
}
free (xfer.remote_nick);
free (xfer.filename);
free (xfer.local_filename);
free (xfer.temp_local_filename);
return result;
}
/*
* Test functions:
* xfer_file_search_crc32
@@ -91,7 +132,30 @@ TEST(XferFile, FindSuffix)
TEST(XferFile, FindFilename)
{
/* TODO: write tests */
char *basename;
config_file_option_set (xfer_config_file_download_path, "/tmp/weechat_test_xfer", 1);
/* remote nick without directory separator: used as-is */
basename = test_find_filename_basename ("alice");
STRCMP_EQUAL("alice.test.txt", basename);
free (basename);
/*
* remote nick with a directory separator: the separator is replaced by
* "_" so the nick cannot make the file be written outside the download
* directory
*/
basename = test_find_filename_basename ("../foo");
STRCMP_EQUAL(".._foo.test.txt", basename);
free (basename);
/* all directory separators in the nick are replaced */
basename = test_find_filename_basename ("a/b/c");
STRCMP_EQUAL("a_b_c.test.txt", basename);
free (basename);
config_file_option_unset (xfer_config_file_download_path);
}
/*
+2 -2
View File
@@ -41,8 +41,8 @@
# devel-number the devel version as hex number ("0x04010000" for "4.1.0-dev")
#
weechat_stable="4.9.1"
weechat_devel="4.9.1"
weechat_stable="4.9.3"
weechat_devel="4.9.3"
stable_major=$(echo "${weechat_stable}" | cut -d"." -f1)
stable_minor=$(echo "${weechat_stable}" | cut -d"." -f2)