Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions src/helpers/CommonCLI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,16 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
file.read((uint8_t *) &_prefs->flood_advert_interval, sizeof(_prefs->flood_advert_interval)); // 125
file.read((uint8_t *) &_prefs->interference_threshold, sizeof(_prefs->interference_threshold)); // 126

// sanitise bad pref values
// sanitize loaded node_name to printable ASCII (defense against legacy invalid values)
{
char filtered[sizeof(_prefs->node_name)];
StrHelper::filterToPrintableASCII(filtered, _prefs->node_name, sizeof(filtered));
if (strcmp(filtered, _prefs->node_name) != 0) {
StrHelper::strncpy(_prefs->node_name, filtered, sizeof(_prefs->node_name));
}
}

// sanitise bad pref numeric values
_prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f);
_prefs->tx_delay_factor = constrain(_prefs->tx_delay_factor, 0, 2.0f);
_prefs->direct_tx_delay_factor = constrain(_prefs->direct_tx_delay_factor, 0, 2.0f);
Expand Down Expand Up @@ -313,9 +322,17 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
strcpy(reply, "Error, invalid key");
}
} else if (memcmp(config, "name ", 5) == 0) {
StrHelper::strncpy(_prefs->node_name, &config[5], sizeof(_prefs->node_name));
savePrefs();
strcpy(reply, "OK");
const char* new_name = &config[5];
size_t n = strlen(new_name);
if (n == 0 || n >= sizeof(_prefs->node_name)) {
strcpy(reply, "Error: name length must be 1-31");
} else if (!StrHelper::isPrintableASCII(new_name)) {
strcpy(reply, "Error: name must be printable ASCII (0x20-0x7E)");
} else {
StrHelper::strncpy(_prefs->node_name, new_name, sizeof(_prefs->node_name));
savePrefs();
strcpy(reply, "OK");
}
} else if (memcmp(config, "repeat ", 7) == 0) {
_prefs->disable_fwd = memcmp(&config[7], "off", 3) == 0;
savePrefs();
Expand Down
36 changes: 32 additions & 4 deletions src/helpers/ESP32Board.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,44 @@

#include <SPIFFS.h>

static void htmlEscape(char* dest, size_t dest_sz, const char* src) {
if (dest_sz == 0) return;
size_t i = 0;
for (; *src && i + 1 < dest_sz; ++src) {
const char* rep = nullptr;
switch (*src) {
case '&': rep = "&amp;"; break;
case '<': rep = "&lt;"; break;
case '>': rep = "&gt;"; break;
case '"': rep = "&quot;"; break;
case 39: rep = "&#39;"; break; // '\''
default: rep = nullptr; break;
}
if (rep) {
for (const char* p = rep; *p && i + 1 < dest_sz; ++p) dest[i++] = *p;
} else {
dest[i++] = *src;
}
}
dest[i] = 0;
}

bool ESP32Board::startOTAUpdate(const char* id, char reply[]) {
WiFi.softAP("MeshCore-OTA", NULL);

sprintf(reply, "Started: http://%s/update", WiFi.softAPIP().toString().c_str());
MESH_DEBUG_PRINTLN("startOTAUpdate: %s", reply);

static char id_buf[60];
sprintf(id_buf, "%s (%s)", id, getManufacturerName());
static char home_buf[90];
sprintf(home_buf, "<H2>Hi! I am a MeshCore Repeater. ID: %s</H2>", id);
// HTML-escape dynamic values to avoid breaking the page with special chars
static char id_safe[128];
static char man_safe[64];
htmlEscape(id_safe, sizeof(id_safe), id);
htmlEscape(man_safe, sizeof(man_safe), getManufacturerName());

static char id_buf[200];
snprintf(id_buf, sizeof(id_buf), "%s (%s)", id_safe, man_safe);
static char home_buf[240];
snprintf(home_buf, sizeof(home_buf), "<H2>Hi! I am a MeshCore Repeater. ID: %s</H2>", id_safe);

AsyncWebServer* server = new AsyncWebServer(80);

Expand Down
24 changes: 24 additions & 0 deletions src/helpers/TxtDataHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,30 @@ void StrHelper::strzcpy(char* dest, const char* src, size_t buf_sz) {
}
}

bool StrHelper::isPrintableASCII(const char* s) {
if (!s) return false;
while (*s) {
unsigned char c = static_cast<unsigned char>(*s++);
if (c < 0x20 || c > 0x7E) return false;
}
return true;
}

void StrHelper::filterToPrintableASCII(char* dest, const char* src, size_t buf_sz) {
if (buf_sz == 0) return;
if (!src) { *dest = 0; return; }
while (buf_sz > 1 && *src) {
unsigned char c = static_cast<unsigned char>(*src++);
if (c >= 0x20 && c <= 0x7E) {
*dest++ = (char)c;
} else {
*dest++ = '_';
}
buf_sz--;
}
*dest = 0;
}

#include <Arduino.h>

union int32_Float_t
Expand Down
4 changes: 4 additions & 0 deletions src/helpers/TxtDataHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,9 @@ class StrHelper {
public:
static void strncpy(char* dest, const char* src, size_t buf_sz);
static void strzcpy(char* dest, const char* src, size_t buf_sz); // pads with trailing nulls
// Returns true if all characters are printable ASCII (0x20-0x7E)
static bool isPrintableASCII(const char* s);
// Copy src to dest, replacing non-printable ASCII bytes with '_'. Always null-terminates.
static void filterToPrintableASCII(char* dest, const char* src, size_t buf_sz);
static const char* ftoa(float f);
};