Harden history device ID validation and SD download filename

This commit is contained in:
2026-02-02 21:19:44 +01:00
parent 0e12b406de
commit a4d9be1903
4 changed files with 119 additions and 3 deletions

View File

@@ -140,6 +140,42 @@ static bool checkbox_checked(const char *name) {
return val == "on" || val == "true" || val == "1";
}
static bool sanitize_history_device_id(const String &input, String &out_device_id) {
if (sanitize_device_id(input, out_device_id)) {
return true;
}
if (g_statuses) {
for (uint8_t i = 0; i < g_status_count; ++i) {
String known = g_statuses[i].last_data.device_id;
if (input.equalsIgnoreCase(known) && sanitize_device_id(known, out_device_id)) {
return true;
}
}
}
return false;
}
static String sanitize_download_filename(const String &input, bool &clean) {
String out;
out.reserve(input.length());
clean = true;
for (size_t i = 0; i < input.length(); ++i) {
unsigned char c = static_cast<unsigned char>(input[i]);
if (c < 32 || c == 127 || c == '"' || c == '\\' || c == '/') {
out += '_';
clean = false;
continue;
}
out += static_cast<char>(c);
}
out.trim();
if (out.length() == 0) {
out = "download.bin";
clean = false;
}
return out;
}
static void history_reset() {
if (g_history.file) {
g_history.file.close();
@@ -597,7 +633,12 @@ static void handle_history_start() {
server.send(200, "application/json", "{\"ok\":false,\"error\":\"time_not_synced\"}");
return;
}
String device_id = server.arg("device_id");
String device_id_arg = server.arg("device_id");
String device_id;
if (!sanitize_history_device_id(device_id_arg, device_id)) {
server.send(200, "application/json", "{\"ok\":false,\"error\":\"bad_device_id\"}");
return;
}
uint16_t days = static_cast<uint16_t>(server.arg("days").toInt());
uint16_t res_min = static_cast<uint16_t>(server.arg("res").toInt());
String mode_str = server.arg("mode");
@@ -652,7 +693,12 @@ static void handle_history_data() {
if (!ensure_auth()) {
return;
}
String device_id = server.arg("device_id");
String device_id_arg = server.arg("device_id");
String device_id;
if (!sanitize_history_device_id(device_id_arg, device_id)) {
server.send(200, "application/json", "{\"ready\":false,\"error\":\"bad_device_id\"}");
return;
}
if (!g_history.bins || device_id.length() == 0 || device_id != g_history.device_id) {
server.send(200, "application/json", "{\"ready\":false,\"error\":\"no_job\"}");
return;
@@ -716,7 +762,11 @@ static void handle_sd_download() {
}
size_t size = f.size();
String filename = path.substring(path.lastIndexOf('/') + 1);
server.sendHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
bool name_clean = true;
(void)name_clean;
String safe_name = sanitize_download_filename(filename, name_clean);
String cd = "attachment; filename=\"" + safe_name + "\"; filename*=UTF-8''" + url_encode_component(safe_name);
server.sendHeader("Content-Disposition", cd);
server.setContentLength(size);
const char *content_type = "application/octet-stream";
if (filename.endsWith(".csv")) {