mirror of
https://github.com/wled/WLED.git
synced 2025-11-08 18:48:53 +00:00
Address final review feedback: revert unnecessary files, use F-strings, add length validation, implement deferred ESP8266 validation
Co-authored-by: netmindz <442066+netmindz@users.noreply.github.com>
This commit is contained in:
@@ -13,7 +13,23 @@ else:
|
|||||||
print('\x1b[6;33;42m' + 'Installing node packages' + '\x1b[0m')
|
print('\x1b[6;33;42m' + 'Installing node packages' + '\x1b[0m')
|
||||||
env.Execute("npm ci")
|
env.Execute("npm ci")
|
||||||
|
|
||||||
print('Building web UI')
|
# Extract the release name from build flags
|
||||||
|
release_name = None # Let cdata.js provide the default if not found
|
||||||
|
build_flags = env.get("BUILD_FLAGS", [])
|
||||||
|
for flag in build_flags:
|
||||||
|
if 'WLED_RELEASE_NAME=' in flag:
|
||||||
|
# Extract the release name, remove quotes and handle different formats
|
||||||
|
parts = flag.split('WLED_RELEASE_NAME=')
|
||||||
|
if len(parts) > 1:
|
||||||
|
release_name = parts[1].split()[0].strip('\"\\')
|
||||||
|
break
|
||||||
|
|
||||||
|
# Set environment variable for cdata.js to use (only if found)
|
||||||
|
if release_name:
|
||||||
|
os.environ['WLED_RELEASE_NAME'] = release_name
|
||||||
|
print(f'Building web UI with release name: {release_name}')
|
||||||
|
else:
|
||||||
|
print('Building web UI with default release name (from cdata.js)')
|
||||||
|
|
||||||
# Call the bundling script
|
# Call the bundling script
|
||||||
exitCode = env.Execute("npm run build")
|
exitCode = env.Execute("npm run build")
|
||||||
|
|||||||
@@ -96,6 +96,10 @@ function adoptVersionAndRepo(html) {
|
|||||||
html = html.replaceAll("##VERSION##", version);
|
html = html.replaceAll("##VERSION##", version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Replace ##RELEASE## with the actual release name from build environment
|
||||||
|
const releaseName = process.env.WLED_RELEASE_NAME || 'Custom';
|
||||||
|
html = html.replaceAll("##RELEASE##", releaseName);
|
||||||
|
|
||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -91,8 +91,7 @@ bool shouldAllowOTA(const uint8_t* binaryData, size_t dataSize, char* errorMessa
|
|||||||
if (!hasCustomDesc) {
|
if (!hasCustomDesc) {
|
||||||
// No custom description - this could be a legacy binary
|
// No custom description - this could be a legacy binary
|
||||||
if (errorMessage && errorMessageLen > 0) {
|
if (errorMessage && errorMessageLen > 0) {
|
||||||
const char* msg = "This firmware file is missing compatibility metadata. Enable 'Ignore firmware validation' to proceed anyway.";
|
strncpy_P(errorMessage, PSTR("This firmware file is missing compatibility metadata. Enable 'Ignore firmware validation' to proceed anyway."), errorMessageLen - 1);
|
||||||
strncpy(errorMessage, msg, errorMessageLen - 1);
|
|
||||||
errorMessage[errorMessageLen - 1] = '\0';
|
errorMessage[errorMessageLen - 1] = '\0';
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -101,7 +100,7 @@ bool shouldAllowOTA(const uint8_t* binaryData, size_t dataSize, char* errorMessa
|
|||||||
// Validate compatibility using extracted release name
|
// Validate compatibility using extracted release name
|
||||||
if (!validateReleaseCompatibility(extractedDesc.release_name)) {
|
if (!validateReleaseCompatibility(extractedDesc.release_name)) {
|
||||||
if (errorMessage && errorMessageLen > 0) {
|
if (errorMessage && errorMessageLen > 0) {
|
||||||
snprintf(errorMessage, errorMessageLen, "Firmware compatibility mismatch: current='%s', uploaded='%s'. Enable 'Ignore firmware validation' to proceed anyway.",
|
snprintf_P(errorMessage, errorMessageLen, PSTR("Firmware compatibility mismatch: current='%s', uploaded='%s'. Enable 'Ignore firmware validation' to proceed anyway."),
|
||||||
releaseString, extractedDesc.release_name);
|
releaseString, extractedDesc.release_name);
|
||||||
errorMessage[errorMessageLen - 1] = '\0'; // Ensure null termination
|
errorMessage[errorMessageLen - 1] = '\0'; // Ensure null termination
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ const wled_custom_desc_t __attribute__((section(WLED_CUSTOM_DESC_SECTION))) wled
|
|||||||
djb2_hash_constexpr(WLED_RELEASE_NAME) // crc32 - computed at compile time
|
djb2_hash_constexpr(WLED_RELEASE_NAME) // crc32 - computed at compile time
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Compile-time validation that release name doesn't exceed maximum length
|
||||||
|
static_assert(sizeof(WLED_RELEASE_NAME) <= WLED_RELEASE_NAME_MAX_LEN,
|
||||||
|
"WLED_RELEASE_NAME exceeds maximum length of WLED_RELEASE_NAME_MAX_LEN characters");
|
||||||
|
|
||||||
// Single reference to ensure it's not optimized away
|
// Single reference to ensure it's not optimized away
|
||||||
const wled_custom_desc_t* __attribute__((used)) wled_custom_desc_ref = &wled_custom_description;
|
const wled_custom_desc_t* __attribute__((used)) wled_custom_desc_ref = &wled_custom_description;
|
||||||
|
|
||||||
|
|||||||
@@ -426,12 +426,28 @@ void initServer()
|
|||||||
}
|
}
|
||||||
if (!correctPIN || otaLock) return;
|
if (!correctPIN || otaLock) return;
|
||||||
|
|
||||||
// Static variable to track release check status across chunks
|
// Static variables to track release check status and data accumulation across chunks
|
||||||
static bool releaseCheckPassed = false;
|
static bool releaseCheckPassed = false;
|
||||||
|
static bool releaseCheckPending = true;
|
||||||
|
static size_t totalBytesReceived = 0;
|
||||||
|
static uint8_t* validationBuffer = nullptr;
|
||||||
|
static const size_t ESP8266_VALIDATION_OFFSET = 0x1000; // 4KB offset for ESP8266
|
||||||
|
static const size_t VALIDATION_BUFFER_SIZE = 8192; // Buffer size for validation
|
||||||
|
|
||||||
if(!index){
|
if(!index){
|
||||||
DEBUG_PRINTLN(F("OTA Update Start"));
|
DEBUG_PRINTLN(F("OTA Update Start"));
|
||||||
|
|
||||||
|
// Reset validation state for new update
|
||||||
|
releaseCheckPassed = false;
|
||||||
|
releaseCheckPending = true;
|
||||||
|
totalBytesReceived = 0;
|
||||||
|
|
||||||
|
// Free any existing validation buffer
|
||||||
|
if (validationBuffer) {
|
||||||
|
free(validationBuffer);
|
||||||
|
validationBuffer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if user wants to skip validation
|
// Check if user wants to skip validation
|
||||||
bool skipValidation = request->hasParam("skipValidation", true);
|
bool skipValidation = request->hasParam("skipValidation", true);
|
||||||
|
|
||||||
@@ -439,10 +455,13 @@ void initServer()
|
|||||||
if (skipValidation) {
|
if (skipValidation) {
|
||||||
DEBUG_PRINTLN(F("OTA validation skipped by user"));
|
DEBUG_PRINTLN(F("OTA validation skipped by user"));
|
||||||
releaseCheckPassed = true;
|
releaseCheckPassed = true;
|
||||||
|
releaseCheckPending = false;
|
||||||
} else {
|
} else {
|
||||||
// Validate OTA release compatibility using the first chunk data directly
|
#ifdef ESP32
|
||||||
|
// ESP32: metadata appears at offset 0, validate immediately with first chunk
|
||||||
char errorMessage[128];
|
char errorMessage[128];
|
||||||
releaseCheckPassed = shouldAllowOTA(data, len, errorMessage, sizeof(errorMessage));
|
releaseCheckPassed = shouldAllowOTA(data, len, errorMessage, sizeof(errorMessage));
|
||||||
|
releaseCheckPending = false;
|
||||||
|
|
||||||
if (!releaseCheckPassed) {
|
if (!releaseCheckPassed) {
|
||||||
DEBUG_PRINTF_P(PSTR("OTA declined: %s\n"), errorMessage);
|
DEBUG_PRINTF_P(PSTR("OTA declined: %s\n"), errorMessage);
|
||||||
@@ -451,6 +470,16 @@ void initServer()
|
|||||||
} else {
|
} else {
|
||||||
DEBUG_PRINTLN(F("OTA allowed: Release compatibility check passed"));
|
DEBUG_PRINTLN(F("OTA allowed: Release compatibility check passed"));
|
||||||
}
|
}
|
||||||
|
#elif defined(ESP8266)
|
||||||
|
// ESP8266: metadata appears around offset 0x1000, need to buffer data until then
|
||||||
|
validationBuffer = (uint8_t*)malloc(VALIDATION_BUFFER_SIZE);
|
||||||
|
if (!validationBuffer) {
|
||||||
|
DEBUG_PRINTLN(F("OTA failed: Could not allocate validation buffer"));
|
||||||
|
request->send(500, FPSTR(CONTENT_TYPE_PLAIN), F("Out of memory for validation"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DEBUG_PRINTLN(F("ESP8266: Deferring validation until offset 0x1000"));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG_PRINTLN(F("Release check passed, starting OTA update"));
|
DEBUG_PRINTLN(F("Release check passed, starting OTA update"));
|
||||||
@@ -487,8 +516,46 @@ void initServer()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write chunk data to OTA update (only if release check passed)
|
// Handle ESP8266 deferred validation - accumulate data until validation offset is reached
|
||||||
if (releaseCheckPassed && !Update.hasError()) {
|
#ifdef ESP8266
|
||||||
|
if (releaseCheckPending && validationBuffer) {
|
||||||
|
// Copy data to validation buffer
|
||||||
|
size_t bytesToCopy = min((size_t)len, VALIDATION_BUFFER_SIZE - totalBytesReceived);
|
||||||
|
if (bytesToCopy > 0) {
|
||||||
|
memcpy(validationBuffer + totalBytesReceived, data, bytesToCopy);
|
||||||
|
}
|
||||||
|
totalBytesReceived += len;
|
||||||
|
|
||||||
|
// Check if we've reached the validation offset
|
||||||
|
if (totalBytesReceived >= ESP8266_VALIDATION_OFFSET + sizeof(wled_custom_desc_t)) {
|
||||||
|
DEBUG_PRINTLN(F("ESP8266: Performing deferred validation"));
|
||||||
|
char errorMessage[128];
|
||||||
|
|
||||||
|
// Validate using buffered data starting from the expected offset
|
||||||
|
releaseCheckPassed = shouldAllowOTA(validationBuffer + ESP8266_VALIDATION_OFFSET,
|
||||||
|
totalBytesReceived - ESP8266_VALIDATION_OFFSET,
|
||||||
|
errorMessage, sizeof(errorMessage));
|
||||||
|
releaseCheckPending = false;
|
||||||
|
|
||||||
|
if (!releaseCheckPassed) {
|
||||||
|
DEBUG_PRINTF_P(PSTR("OTA declined (deferred): %s\n"), errorMessage);
|
||||||
|
free(validationBuffer);
|
||||||
|
validationBuffer = nullptr;
|
||||||
|
request->send(400, FPSTR(CONTENT_TYPE_PLAIN), errorMessage);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
DEBUG_PRINTLN(F("OTA allowed: Deferred release compatibility check passed"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free validation buffer as it's no longer needed
|
||||||
|
free(validationBuffer);
|
||||||
|
validationBuffer = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Write chunk data to OTA update (only if release check passed or still pending)
|
||||||
|
if ((releaseCheckPassed || releaseCheckPending) && !Update.hasError()) {
|
||||||
if (Update.write(data, len) != len) {
|
if (Update.write(data, len) != len) {
|
||||||
DEBUG_PRINTF_P(PSTR("OTA write failed on chunk %zu: %s\n"), index, Update.getErrorString().c_str());
|
DEBUG_PRINTF_P(PSTR("OTA write failed on chunk %zu: %s\n"), index, Update.getErrorString().c_str());
|
||||||
}
|
}
|
||||||
@@ -497,6 +564,21 @@ void initServer()
|
|||||||
if(isFinal){
|
if(isFinal){
|
||||||
DEBUG_PRINTLN(F("OTA Update End"));
|
DEBUG_PRINTLN(F("OTA Update End"));
|
||||||
|
|
||||||
|
// Clean up validation buffer if still allocated
|
||||||
|
#ifdef ESP8266
|
||||||
|
if (validationBuffer) {
|
||||||
|
free(validationBuffer);
|
||||||
|
validationBuffer = nullptr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Check if validation was still pending (shouldn't happen normally)
|
||||||
|
if (releaseCheckPending) {
|
||||||
|
DEBUG_PRINTLN(F("OTA failed: Validation never completed"));
|
||||||
|
request->send(400, FPSTR(CONTENT_TYPE_PLAIN), F("Firmware validation incomplete"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (releaseCheckPassed) {
|
if (releaseCheckPassed) {
|
||||||
if(Update.end(true)){
|
if(Update.end(true)){
|
||||||
DEBUG_PRINTLN(F("Update Success"));
|
DEBUG_PRINTLN(F("Update Success"));
|
||||||
|
|||||||
Reference in New Issue
Block a user