From 68ffb3235454a4406aa0c8ca533c38f3376103da Mon Sep 17 00:00:00 2001 From: Kemal Hadimli Date: Tue, 9 Jun 2026 20:50:58 +0100 Subject: [PATCH] Harden channel filter: bounds-guard decrypt buffer, match keywords across whole message Guard data[len]=0 against an over-long crafted payload (OOB store), and match keywords against the whole folded message so a blocked word can't be hidden in the self-declared sender name. Also drop the unguarded ctype.h. Co-Authored-By: Claude Opus 4.8 --- docs/cli_commands.md | 4 ++-- examples/simple_repeater/MyMesh.cpp | 15 ++++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 4603e78ba..374899e2c 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -1155,9 +1155,9 @@ Repeater only. Lets a repeater decrypt channels it holds the key for, inspect th - `filter block ` **Parameters:** -- `keyword`: Text to match (case-insensitive substring) against the message body. Max 23 characters. +- `keyword`: Text to match (case-insensitive substring) against the whole message. Max 23 characters. -**Note:** Matches against the message text only, not the sender name. +**Note:** Matches anywhere in the message — both the body and the sender name — so a blocked word can't be hidden in a self-declared sender name. --- diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index f0d7e6be7..9e2fb6d89 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -1,6 +1,5 @@ #include "MyMesh.h" #include -#include #ifdef WITH_CHANNEL_FILTER #include "UnicodeFold.h" @@ -703,17 +702,19 @@ void MyMesh::onGroupDataRecv(mesh::Packet *packet, uint8_t type, const mesh::Gro if (type != PAYLOAD_TYPE_GRP_TXT) return; // only inspect channel text messages if (len < 6) return; if ((data[4] >> 2) != 0) return; // not a plain-text message + if (len >= MAX_PACKET_PAYLOAD) return; // crafted over-long payload; avoid OOB on data[len] data[len] = 0; // make a C string: "sender_name: text" const char *msg = (const char *)&data[5]; const char *sep = strstr(msg, ": "); - const char *text = sep ? sep + 2 : msg; - // Unicode-fold both sides so homoglyph / zero-width tricks can't evade the - // blocklist (see UnicodeFold.h). Terms are folded the same way at match time. - char folded_text[MAX_PACKET_PAYLOAD]; - ufold::foldUtf8(text, folded_text, sizeof(folded_text)); + // Unicode-fold so homoglyph / zero-width tricks can't evade the blocklist (see + // UnicodeFold.h). Keywords match the whole message (sender + text) so a blocked + // word can't be hidden in the self-declared sender name. Terms are folded the + // same way at match time. + char folded_msg[MAX_PACKET_PAYLOAD]; + ufold::foldUtf8(msg, folded_msg, sizeof(folded_msg)); char folded_sender[40]; folded_sender[0] = 0; @@ -736,7 +737,7 @@ void MyMesh::onGroupDataRecv(mesh::Packet *packet, uint8_t type, const mesh::Gro } for (int i = 0; i < num_block_keywords && !blocked; i++) { ufold::foldUtf8(block_keywords[i], fterm, sizeof(fterm)); - if (fterm[0] && strstr(folded_text, fterm)) blocked = true; + if (fterm[0] && strstr(folded_msg, fterm)) blocked = true; } if (blocked) {