Skip to content

fix: escape error JSON responses, check write/OTA return values (#77, #78)#89

Merged
sjordan0228 merged 2 commits into
devfrom
feature/web-ui-hardening
Apr 2, 2026
Merged

fix: escape error JSON responses, check write/OTA return values (#77, #78)#89
sjordan0228 merged 2 commits into
devfrom
feature/web-ui-hardening

Conversation

@sjordan0228
Copy link
Copy Markdown
Contributor

@sjordan0228 sjordan0228 commented Apr 2, 2026

Summary

Test Plan

  • pio run -e esp32dev compiles clean
  • pio run -e esp32s3zero compiles clean
  • Error responses with special characters produce valid JSON
  • Format tag with full write queue returns 503
  • TigerTag write with full queue returns 503
  • OTA update from URL with insufficient heap returns 500

Closes #77, closes #78

Summary by CodeRabbit

  • Bug Fixes
    • OTA updates now detect and report failures when the update task fails to start, returning an error instead of a false success.
    • NFC tag formatting/writing now returns a "write queue full" error when the write queue is saturated.
    • API error responses are escaped and restructured to prevent malformed JSON and ensure valid error payloads.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 2, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4c3c391a-e707-4b33-814e-b87d0f55c346

📥 Commits

Reviewing files that changed from the base of the PR and between 8280268 and c703dc6.

📒 Files selected for processing (1)
  • src/WebServerManager.cpp

📝 Walkthrough

Walkthrough

Fixes in WebServerManager: check OTA task creation result, enforce NFC write queue backpressure, and escape/serialize error messages safely into JSON to avoid malformed responses.

Changes

Cohort / File(s) Summary
WebServerManager updates
src/WebServerManager.cpp
handleApiUpdateFromUrl() now checks xTaskCreatePinnedToCore() result and returns 500 on failure; handleApiFormatTag() and handleApiWriteTigerTag() check NFCManager::getInstance().enqueueWrite() and return 503 when the write queue is full; sendError() now escapes ", \, control chars (e.g., \n, \r, \t, and other <0x20`) before embedding into JSON and uses larger buffers to build the payload.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and clearly summarizes the two main fixes: escaping error JSON responses and checking return values for write/OTA operations, with issue references.
Description check ✅ Passed The description covers both issues, explains the changes and fixes, and includes a test plan matching the template structure with compilation and functional verification steps.
Linked Issues check ✅ Passed All code changes directly address the requirements in #77 (JSON escaping) and #78 (return value checks for enqueueWrite and xTaskCreatePinnedToCore), with appropriate error responses.
Out of Scope Changes check ✅ Passed All changes in WebServerManager.cpp are directly related to fixing the two linked issues; no unrelated modifications are present.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/web-ui-hardening

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/WebServerManager.cpp (1)

414-417: ⚠️ Potential issue | 🟠 Major

Inconsistent: raw JSON concatenation bypasses the escaping fix.

This line constructs JSON by concatenating errMsg, which includes the raw Spoolman response body. This is exactly the vulnerability issue #77 describes—if the upstream response contains quotes or newlines, the JSON becomes malformed. The sendError helper should be used here instead.

🛠️ Proposed fix
-        String errMsg = "Failed to create spool (HTTP " + String(code) + "): " + response;
         xSemaphoreGive(g_httpMutex);
-        _server.send(500, "application/json", "{\"error\":\"" + errMsg + "\"}");
+        char errMsg[128];
+        snprintf(errMsg, sizeof(errMsg), "Failed to create spool (HTTP %d)", code);
+        sendError(500, errMsg);
         return;

Note: The raw response body is intentionally omitted from the error message since it can be arbitrarily long and contain uncontrolled characters. If the response content is needed for debugging, log it server-side instead of sending it to the client.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/WebServerManager.cpp` around lines 414 - 417, The JSON is built by
concatenating errMsg (which contains the raw Spoolman response) causing
malformed/unsafe JSON; replace the manual _server.send(500, "application/json",
"{\"error\":\"" + errMsg + "\"}") call with the existing sendError helper (e.g.,
call sendError(500, "Failed to create spool (HTTP " + String(code) + ")") or
similar) and release the mutex as before with xSemaphoreGive(g_httpMutex); log
the full raw response server-side (using your existing logger) rather than
including it in the JSON sent to the client so arbitrary characters/length from
response are not injected into the JSON body.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/WebServerManager.cpp`:
- Around line 1526-1539: The current manual escaping in
WebServerManager::sendError (variables msg, escaped, body) only handles quotes
and backslashes and fails for newlines/control characters; replace the manual
escape+snprintf logic with ArduinoJson: create a StaticJsonDocument (e.g., 256),
set doc["success"]=false and doc["error"]=msg, serializeJson to a String, call
_server.sendHeader("Access-Control-Allow-Origin","*") and then _server.send with
"application/json" and the serialized body; remove the escaped/body buffers and
loop entirely so ArduinoJson handles all necessary escaping.

---

Outside diff comments:
In `@src/WebServerManager.cpp`:
- Around line 414-417: The JSON is built by concatenating errMsg (which contains
the raw Spoolman response) causing malformed/unsafe JSON; replace the manual
_server.send(500, "application/json", "{\"error\":\"" + errMsg + "\"}") call
with the existing sendError helper (e.g., call sendError(500, "Failed to create
spool (HTTP " + String(code) + ")") or similar) and release the mutex as before
with xSemaphoreGive(g_httpMutex); log the full raw response server-side (using
your existing logger) rather than including it in the JSON sent to the client so
arbitrary characters/length from response are not injected into the JSON body.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3c5c80bd-718b-4be5-9d37-d1717f4b10db

📥 Commits

Reviewing files that changed from the base of the PR and between 1149476 and 8280268.

📒 Files selected for processing (1)
  • src/WebServerManager.cpp

Comment thread src/WebServerManager.cpp Outdated
Comment on lines +1526 to +1539
// Escape quotes and backslashes in msg to produce valid JSON
char escaped[128];
size_t j = 0;
for (size_t i = 0; msg[i] && j < sizeof(escaped) - 2; i++) {
if (msg[i] == '"' || msg[i] == '\\') {
escaped[j++] = '\\';
}
if (j < sizeof(escaped) - 1) {
escaped[j++] = msg[i];
}
}
escaped[j] = '\0';
char body[192];
snprintf(body, sizeof(body), "{\"success\":false,\"error\":\"%s\"}", escaped);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Incomplete escaping: newlines and control characters are not handled.

Issue #77 explicitly mentions escaping "quotes, backslashes, newlines" but this implementation only handles quotes and backslashes. If an upstream error contains \n, \r, \t, or other control characters, the JSON will still be malformed.

🛠️ Proposed fix to escape control characters
 void WebServerManager::sendError(int code, const char* msg) {
     _server.sendHeader("Access-Control-Allow-Origin", "*");
     // Escape quotes and backslashes in msg to produce valid JSON
-    char escaped[128];
+    char escaped[256];
     size_t j = 0;
-    for (size_t i = 0; msg[i] && j < sizeof(escaped) - 2; i++) {
-        if (msg[i] == '"' || msg[i] == '\\') {
-            escaped[j++] = '\\';
-        }
-        if (j < sizeof(escaped) - 1) {
-            escaped[j++] = msg[i];
+    for (size_t i = 0; msg[i] && j < sizeof(escaped) - 6; i++) {
+        char c = msg[i];
+        if (c == '"' || c == '\\') {
+            escaped[j++] = '\\';
+            escaped[j++] = c;
+        } else if (c == '\n') {
+            escaped[j++] = '\\';
+            escaped[j++] = 'n';
+        } else if (c == '\r') {
+            escaped[j++] = '\\';
+            escaped[j++] = 'r';
+        } else if (c == '\t') {
+            escaped[j++] = '\\';
+            escaped[j++] = 't';
+        } else if ((unsigned char)c < 0x20) {
+            // Other control chars: encode as \uXXXX
+            j += snprintf(escaped + j, sizeof(escaped) - j, "\\u%04X", (unsigned char)c);
+        } else {
+            escaped[j++] = c;
         }
     }
     escaped[j] = '\0';
-    char body[192];
+    char body[320];
     snprintf(body, sizeof(body), "{\"success\":false,\"error\":\"%s\"}", escaped);
     _server.send(code, "application/json", body);
 }

Alternatively, consider using ArduinoJson (already included) to build the error response safely:

void WebServerManager::sendError(int code, const char* msg) {
    _server.sendHeader("Access-Control-Allow-Origin", "*");
    StaticJsonDocument<256> doc;
    doc["success"] = false;
    doc["error"] = msg;
    String body;
    serializeJson(doc, body);
    _server.send(code, "application/json", body);
}

This leverages ArduinoJson's built-in escaping and is consistent with other response handlers in this file.

🧰 Tools
🪛 Clang (14.0.6)

[warning] 1527-1527: do not declare C-style arrays, use std::array<> instead

(modernize-avoid-c-arrays)


[warning] 1528-1528: variable 'j' is not initialized

(cppcoreguidelines-init-variables)


[warning] 1528-1528: variable name 'j' is too short, expected at least 3 characters

(readability-identifier-length)


[warning] 1538-1538: do not declare C-style arrays, use std::array<> instead

(modernize-avoid-c-arrays)


[warning] 1539-1539: escaped string literal can be written as a raw string literal

(modernize-raw-string-literal)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/WebServerManager.cpp` around lines 1526 - 1539, The current manual
escaping in WebServerManager::sendError (variables msg, escaped, body) only
handles quotes and backslashes and fails for newlines/control characters;
replace the manual escape+snprintf logic with ArduinoJson: create a
StaticJsonDocument (e.g., 256), set doc["success"]=false and doc["error"]=msg,
serializeJson to a String, call
_server.sendHeader("Access-Control-Allow-Origin","*") and then _server.send with
"application/json" and the serialized body; remove the escaped/body buffers and
loop entirely so ArduinoJson handles all necessary escaping.

@sjordan0228 sjordan0228 merged commit 76c4972 into dev Apr 2, 2026
1 check was pending
@sjordan0228 sjordan0228 deleted the feature/web-ui-hardening branch April 2, 2026 19:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant