diff mbox series

hawkbit: Escape JSON special characters in deploymentBase feedback details

Message ID 7caae01a-66b3-48ca-9dc2-c317353daeac@atmark-techno.com
State New
Headers show
Series hawkbit: Escape JSON special characters in deploymentBase feedback details | expand

Commit Message

Shota Shimoyama Sept. 14, 2026, 8:54 a.m. UTC
When sending a POST request with deployment feedback to hawkBit,
the payload for "details" was built by raw string concatenation of
TRACE/ERROR messages. If these messages contained JSON special characters,
the generated JSON became invalid, causing hawkBit to return a 400 Bad
Request with the following response body:

{
    "errorCode": "hawkbit.server.error.rest.body.notReadable",
    "exceptionClass": "org.eclipse.hawkbit.rest.exception.MessageNotReadableException",
    "message": "The given request body is not well formed"
}

Particularly when this error occurs during feedback requests intended to
notify final statuses like "finished" or "error", hawkBit fails to register
the completion of the action. Consequently, hawkBit keeps the action active,
causing SWUpdate to repeatedly attempt the update at every polling interval.

To ensure robust serialization of arbitrary script outputs (stdout/stderr)
from run_system_cmd(), modify server_create_details() to construct the JSON
array using json-c. This guarantees proper JSON escaping for all special
characters in the deploymentBase feedback details.

Signed-off-by: Shota Shimoyama <shota.shimoyama@atmark-techno.com>
---
 suricatta/server_hawkbit.c | 36 ++++++++++++++++++++++--------------
 1 file changed, 22 insertions(+), 14 deletions(-)

Comments

Dominique Martinet Sept. 15, 2026, 6:21 a.m. UTC | #1
Shota Shimoyama wrote on Mon, Sep 14, 2026 at 05:54:57PM +0900:
> When sending a POST request with deployment feedback to hawkBit,
> the payload for "details" was built by raw string concatenation of
> TRACE/ERROR messages. If these messages contained JSON special characters,
> the generated JSON became invalid, causing hawkBit to return a 400 Bad
> Request with the following response body:
> 
> {
>     "errorCode": "hawkbit.server.error.rest.body.notReadable",
>     "exceptionClass": "org.eclipse.hawkbit.rest.exception.MessageNotReadableException",
>     "message": "The given request body is not well formed"
> }
> 
> Particularly when this error occurs during feedback requests intended to
> notify final statuses like "finished" or "error", hawkBit fails to register
> the completion of the action. Consequently, hawkBit keeps the action active,
> causing SWUpdate to repeatedly attempt the update at every polling interval.
> 
> To ensure robust serialization of arbitrary script outputs (stdout/stderr)
> from run_system_cmd(), modify server_create_details() to construct the JSON
> array using json-c. This guarantees proper JSON escaping for all special
> characters in the deploymentBase feedback details.
> 
> Signed-off-by: Shota Shimoyama <shota.shimoyama@atmark-techno.com>

Thanks!

Reviewed-by: Dominique Martinet <dominique.martinet@atmark-techno.com>

I spotted a leak by chance looking at this diff so I sent another patch
for it (not related to this patch)
diff mbox series

Patch

diff --git a/suricatta/server_hawkbit.c b/suricatta/server_hawkbit.c
index a9d09ac5..781c023a 100644
--- a/suricatta/server_hawkbit.c
+++ b/suricatta/server_hawkbit.c
@@ -367,27 +367,35 @@  cleanup:
 
 static char *server_create_details(int numdetails, const char *details[])
 {
-	int i, ret;
-	char *prev = NULL;
-	char *next = NULL;
+	int i;
+	struct json_object *jarray = NULL;
+	struct json_object *jstr = NULL;
+	char *result = NULL;
 
 	/*
 	 * Note: NEVER call TRACE / ERROR inside this function
 	 * because it generates a recursion
 	 */
+
+	jarray = json_object_new_array_ext(numdetails);
+	if (!jarray)
+		return NULL;
+
 	for (i = 0; i < numdetails; i++) {
-		if (i == 0) {
-			ret = asprintf(&next, "\"%s\"", details[i]);
-		} else {
-			ret = asprintf(&next, "%s,\"%s\"", prev, details[i]);
-			free(prev);
-		}
-		if (ret == ENOMEM_ASPRINTF)
+		jstr = json_object_new_string(details[i]);
+		if (!jstr) {
+			json_object_put(jarray);
 			return NULL;
-		prev = next;
+		}
+		json_object_array_add(jarray, jstr);
 	}
 
-	return next;
+	const char *json_str = json_object_to_json_string(jarray);
+
+	result = json_str ? strdup(json_str) : NULL;
+
+	json_object_put(jarray);
+	return result;
 }
 
 server_op_res_t
@@ -421,7 +429,7 @@  server_send_deployment_reply(channel_t *channel,
 				"finished": "%s"
 			},
 			"execution": "%s",
-			"details" : [ %s ]
+			"details" : %s
 		}
 	}
 	);
@@ -436,7 +444,7 @@  server_send_deployment_reply(channel_t *channel,
 	if (ENOMEM_ASPRINTF ==
 	    asprintf(&json_reply_string, json_hawkbit_deployment_feedback,
 		     action_id, fdate, job_cnt_cur, job_cnt_max, finished,
-		     execution_status, detail ? detail : " ")) {
+		     execution_status, detail ? detail : "[]")) {
 		ERROR("hawkBit server reply cannot be sent because of OOM.");
 		result = SERVER_EINIT;
 		goto cleanup;