upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYour Name <you@example.com>2026-05-18 23:20:22 +0530
committerYour Name <you@example.com>2026-05-18 23:20:22 +0530
commit980c98d2c9dbff7b4d87c867c6638637e063f984 (patch)
treeeee75aa12748c7e83794378213df13eec5d974df
parent83967b17b234629fc0a3679296cafda08f5a9f37 (diff)
fix: CVM subscription #p array format + WS response + use-after-free
Critical fixes for CVM MCP roundtrip over Nostr relay: - Fix subscription filter: #p must be array not string (relay rejected with 'bad req') - Publish MCP responses via existing WS connection instead of opening new TLS - Fix use-after-free: tags_str freed before nostr_event_to_json used it - Pass esp_tls_t through process_relay_message -> handle_mcp_message chain Verified on Board B via relay.primal.net: - MCP initialize roundtrip: PASS - tools/call get_config: PASS (returns device config) - tools/call get_balance: PASS (returns balance_sats, proof_count) - CEP-6 announcements (kinds 11316, 11317, 10002): all accepted by relay
-rw-r--r--main/cvm_server.c78
1 files changed, 69 insertions, 9 deletions
diff --git a/main/cvm_server.c b/main/cvm_server.c
index dd04047..96ce7d3 100644
--- a/main/cvm_server.c
+++ b/main/cvm_server.c
@@ -323,6 +323,54 @@ static esp_err_t publish_event_to_relay(const char *relay_url, const char *event
323 return ESP_OK; 323 return ESP_OK;
324} 324}
325 325
326static esp_err_t publish_kind_25910_response_ws(esp_tls_t *tls,
327 const char *content_json,
328 const char *request_event_id)
329{
330 const tollgate_identity_t *id = identity_get();
331 if (!id || !id->initialized) return ESP_FAIL;
332
333 cJSON *tags = cJSON_CreateArray();
334 cJSON *e_tag = cJSON_CreateArray();
335 cJSON_AddItemToArray(e_tag, cJSON_CreateString("e"));
336 cJSON_AddItemToArray(e_tag, cJSON_CreateString(request_event_id));
337 cJSON_AddItemToArray(tags, e_tag);
338
339 char *tags_str = cJSON_PrintUnformatted(tags);
340 cJSON_Delete(tags);
341
342 nostr_event_t event;
343 nostr_event_init(&event, id->npub_hex, 25910, tags_str, content_json);
344 nostr_event_sign(&event, id->nsec);
345
346 char *event_json = malloc(8192);
347 if (!event_json) {
348 free(tags_str);
349 return ESP_ERR_NO_MEM;
350 }
351
352 esp_err_t ret = nostr_event_to_json(&event, event_json, 8192);
353 free(tags_str);
354 if (ret != ESP_OK) {
355 free(event_json);
356 return ret;
357 }
358
359 size_t msg_len = 10 + strlen(event_json) + 2;
360 char *msg = malloc(msg_len);
361 if (!msg) {
362 free(event_json);
363 return ESP_ERR_NO_MEM;
364 }
365 snprintf(msg, msg_len, "[\"EVENT\",%s]", event_json);
366 ESP_LOGI(TAG, "Sending WS response (%d bytes)", (int)strlen(msg));
367 int rc = ws_send_text(tls, msg);
368 ESP_LOGI(TAG, "WS send result: %d", rc);
369 free(msg);
370 free(event_json);
371 return ESP_OK;
372}
373
326static esp_err_t publish_kind_25910_response(const char *relay_url, 374static esp_err_t publish_kind_25910_response(const char *relay_url,
327 const char *content_json, 375 const char *content_json,
328 const char *request_event_id) 376 const char *request_event_id)
@@ -366,7 +414,7 @@ static bool is_owner_pubkey(const char *pubkey_hex)
366 return strcmp(id->npub_hex, pubkey_hex) == 0; 414 return strcmp(id->npub_hex, pubkey_hex) == 0;
367} 415}
368 416
369static void handle_mcp_message(const char *relay_url, const char *sender_pubkey, 417static void handle_mcp_message(esp_tls_t *tls, const char *sender_pubkey,
370 const char *event_id, const char *content) 418 const char *event_id, const char *content)
371{ 419{
372 cJSON *msg = cJSON_Parse(content); 420 cJSON *msg = cJSON_Parse(content);
@@ -386,14 +434,20 @@ static void handle_mcp_message(const char *relay_url, const char *sender_pubkey,
386 if (strcmp(m, "initialize") == 0) { 434 if (strcmp(m, "initialize") == 0) {
387 ESP_LOGI(TAG, "MCP initialize from %s", sender_pubkey); 435 ESP_LOGI(TAG, "MCP initialize from %s", sender_pubkey);
388 char *resp = build_initialize_response(id_str, sender_pubkey); 436 char *resp = build_initialize_response(id_str, sender_pubkey);
389 publish_kind_25910_response(relay_url, resp, event_id); 437 if (tls) {
438 publish_kind_25910_response_ws(tls, resp, event_id);
439 } else {
440 ESP_LOGW(TAG, "No TLS for response");
441 }
390 free(resp); 442 free(resp);
391 } else if (strcmp(m, "notifications/initialized") == 0) { 443 } else if (strcmp(m, "notifications/initialized") == 0) {
392 ESP_LOGI(TAG, "Client initialized: %s", sender_pubkey); 444 ESP_LOGI(TAG, "Client initialized: %s", sender_pubkey);
393 } else if (strcmp(m, "tools/list") == 0) { 445 } else if (strcmp(m, "tools/list") == 0) {
394 ESP_LOGI(TAG, "tools/list from %s", sender_pubkey); 446 ESP_LOGI(TAG, "tools/list from %s", sender_pubkey);
395 char *resp = build_tools_list_response(id_str); 447 char *resp = build_tools_list_response(id_str);
396 publish_kind_25910_response(relay_url, resp, event_id); 448 if (tls) {
449 publish_kind_25910_response_ws(tls, resp, event_id);
450 }
397 free(resp); 451 free(resp);
398 } else if (strcmp(m, "tools/call") == 0) { 452 } else if (strcmp(m, "tools/call") == 0) {
399 cJSON *params = cJSON_GetObjectItem(msg, "params"); 453 cJSON *params = cJSON_GetObjectItem(msg, "params");
@@ -414,12 +468,16 @@ static void handle_mcp_message(const char *relay_url, const char *sender_pubkey,
414 468
415 mcp_response_t mcp_resp = mcp_dispatch(&req); 469 mcp_response_t mcp_resp = mcp_dispatch(&req);
416 char *resp = build_tool_call_response(id_str, &mcp_resp); 470 char *resp = build_tool_call_response(id_str, &mcp_resp);
417 publish_kind_25910_response(relay_url, resp, event_id); 471 if (tls) {
472 publish_kind_25910_response_ws(tls, resp, event_id);
473 }
418 free(resp); 474 free(resp);
419 } 475 }
420 } else if (strcmp(m, "ping") == 0) { 476 } else if (strcmp(m, "ping") == 0) {
421 char *resp = build_ping_response(id_str); 477 char *resp = build_ping_response(id_str);
422 publish_kind_25910_response(relay_url, resp, event_id); 478 if (tls) {
479 publish_kind_25910_response_ws(tls, resp, event_id);
480 }
423 free(resp); 481 free(resp);
424 } else { 482 } else {
425 ESP_LOGW(TAG, "Unknown MCP method: %s", m); 483 ESP_LOGW(TAG, "Unknown MCP method: %s", m);
@@ -433,7 +491,7 @@ static void handle_mcp_message(const char *relay_url, const char *sender_pubkey,
433 cJSON_Delete(msg); 491 cJSON_Delete(msg);
434} 492}
435 493
436static void process_relay_message(const char *relay_url, const char *msg_str) 494static void process_relay_message(esp_tls_t *tls, const char *relay_url, const char *msg_str)
437{ 495{
438 cJSON *arr = cJSON_Parse(msg_str); 496 cJSON *arr = cJSON_Parse(msg_str);
439 if (!arr || !cJSON_IsArray(arr)) { 497 if (!arr || !cJSON_IsArray(arr)) {
@@ -492,7 +550,7 @@ static void process_relay_message(const char *relay_url, const char *msg_str)
492 return; 550 return;
493 } 551 }
494 552
495 handle_mcp_message(relay_url, pubkey->valuestring, event_id->valuestring, content->valuestring); 553 handle_mcp_message(tls, pubkey->valuestring, event_id->valuestring, content->valuestring);
496 cJSON_Delete(arr); 554 cJSON_Delete(arr);
497} 555}
498 556
@@ -505,7 +563,9 @@ static esp_err_t subscribe_to_relay(esp_tls_t *tls, const char *npub)
505 cJSON *kinds = cJSON_CreateArray(); 563 cJSON *kinds = cJSON_CreateArray();
506 cJSON_AddItemToArray(kinds, cJSON_CreateNumber(25910)); 564 cJSON_AddItemToArray(kinds, cJSON_CreateNumber(25910));
507 cJSON_AddItemToObject(filter, "kinds", kinds); 565 cJSON_AddItemToObject(filter, "kinds", kinds);
508 cJSON_AddStringToObject(filter, "#p", npub); 566 cJSON *p_tags = cJSON_CreateArray();
567 cJSON_AddItemToArray(p_tags, cJSON_CreateString(npub));
568 cJSON_AddItemToObject(filter, "#p", p_tags);
509 cJSON_AddNumberToObject(filter, "limit", 100); 569 cJSON_AddNumberToObject(filter, "limit", 100);
510 cJSON_AddItemToArray(sub, filter); 570 cJSON_AddItemToArray(sub, filter);
511 571
@@ -567,7 +627,7 @@ static void cvm_relay_task(void *arg)
567 char *text = parse_ws_text_frame(buf, rlen); 627 char *text = parse_ws_text_frame(buf, rlen);
568 if (text) { 628 if (text) {
569 if (strlen(text) > 0) { 629 if (strlen(text) > 0) {
570 process_relay_message(relay_url, text); 630 process_relay_message(tls, relay_url, text);
571 } 631 }
572 free(text); 632 free(text);
573 } 633 }