From 980c98d2c9dbff7b4d87c867c6638637e063f984 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 18 May 2026 23:20:22 +0530 Subject: 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 --- main/cvm_server.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file 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 return ESP_OK; } +static esp_err_t publish_kind_25910_response_ws(esp_tls_t *tls, + const char *content_json, + const char *request_event_id) +{ + const tollgate_identity_t *id = identity_get(); + if (!id || !id->initialized) return ESP_FAIL; + + cJSON *tags = cJSON_CreateArray(); + cJSON *e_tag = cJSON_CreateArray(); + cJSON_AddItemToArray(e_tag, cJSON_CreateString("e")); + cJSON_AddItemToArray(e_tag, cJSON_CreateString(request_event_id)); + cJSON_AddItemToArray(tags, e_tag); + + char *tags_str = cJSON_PrintUnformatted(tags); + cJSON_Delete(tags); + + nostr_event_t event; + nostr_event_init(&event, id->npub_hex, 25910, tags_str, content_json); + nostr_event_sign(&event, id->nsec); + + char *event_json = malloc(8192); + if (!event_json) { + free(tags_str); + return ESP_ERR_NO_MEM; + } + + esp_err_t ret = nostr_event_to_json(&event, event_json, 8192); + free(tags_str); + if (ret != ESP_OK) { + free(event_json); + return ret; + } + + size_t msg_len = 10 + strlen(event_json) + 2; + char *msg = malloc(msg_len); + if (!msg) { + free(event_json); + return ESP_ERR_NO_MEM; + } + snprintf(msg, msg_len, "[\"EVENT\",%s]", event_json); + ESP_LOGI(TAG, "Sending WS response (%d bytes)", (int)strlen(msg)); + int rc = ws_send_text(tls, msg); + ESP_LOGI(TAG, "WS send result: %d", rc); + free(msg); + free(event_json); + return ESP_OK; +} + static esp_err_t publish_kind_25910_response(const char *relay_url, const char *content_json, const char *request_event_id) @@ -366,7 +414,7 @@ static bool is_owner_pubkey(const char *pubkey_hex) return strcmp(id->npub_hex, pubkey_hex) == 0; } -static void handle_mcp_message(const char *relay_url, const char *sender_pubkey, +static void handle_mcp_message(esp_tls_t *tls, const char *sender_pubkey, const char *event_id, const char *content) { cJSON *msg = cJSON_Parse(content); @@ -386,14 +434,20 @@ static void handle_mcp_message(const char *relay_url, const char *sender_pubkey, if (strcmp(m, "initialize") == 0) { ESP_LOGI(TAG, "MCP initialize from %s", sender_pubkey); char *resp = build_initialize_response(id_str, sender_pubkey); - publish_kind_25910_response(relay_url, resp, event_id); + if (tls) { + publish_kind_25910_response_ws(tls, resp, event_id); + } else { + ESP_LOGW(TAG, "No TLS for response"); + } free(resp); } else if (strcmp(m, "notifications/initialized") == 0) { ESP_LOGI(TAG, "Client initialized: %s", sender_pubkey); } else if (strcmp(m, "tools/list") == 0) { ESP_LOGI(TAG, "tools/list from %s", sender_pubkey); char *resp = build_tools_list_response(id_str); - publish_kind_25910_response(relay_url, resp, event_id); + if (tls) { + publish_kind_25910_response_ws(tls, resp, event_id); + } free(resp); } else if (strcmp(m, "tools/call") == 0) { cJSON *params = cJSON_GetObjectItem(msg, "params"); @@ -414,12 +468,16 @@ static void handle_mcp_message(const char *relay_url, const char *sender_pubkey, mcp_response_t mcp_resp = mcp_dispatch(&req); char *resp = build_tool_call_response(id_str, &mcp_resp); - publish_kind_25910_response(relay_url, resp, event_id); + if (tls) { + publish_kind_25910_response_ws(tls, resp, event_id); + } free(resp); } } else if (strcmp(m, "ping") == 0) { char *resp = build_ping_response(id_str); - publish_kind_25910_response(relay_url, resp, event_id); + if (tls) { + publish_kind_25910_response_ws(tls, resp, event_id); + } free(resp); } else { 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, cJSON_Delete(msg); } -static void process_relay_message(const char *relay_url, const char *msg_str) +static void process_relay_message(esp_tls_t *tls, const char *relay_url, const char *msg_str) { cJSON *arr = cJSON_Parse(msg_str); if (!arr || !cJSON_IsArray(arr)) { @@ -492,7 +550,7 @@ static void process_relay_message(const char *relay_url, const char *msg_str) return; } - handle_mcp_message(relay_url, pubkey->valuestring, event_id->valuestring, content->valuestring); + handle_mcp_message(tls, pubkey->valuestring, event_id->valuestring, content->valuestring); cJSON_Delete(arr); } @@ -505,7 +563,9 @@ static esp_err_t subscribe_to_relay(esp_tls_t *tls, const char *npub) cJSON *kinds = cJSON_CreateArray(); cJSON_AddItemToArray(kinds, cJSON_CreateNumber(25910)); cJSON_AddItemToObject(filter, "kinds", kinds); - cJSON_AddStringToObject(filter, "#p", npub); + cJSON *p_tags = cJSON_CreateArray(); + cJSON_AddItemToArray(p_tags, cJSON_CreateString(npub)); + cJSON_AddItemToObject(filter, "#p", p_tags); cJSON_AddNumberToObject(filter, "limit", 100); cJSON_AddItemToArray(sub, filter); @@ -567,7 +627,7 @@ static void cvm_relay_task(void *arg) char *text = parse_ws_text_frame(buf, rlen); if (text) { if (strlen(text) > 0) { - process_relay_message(relay_url, text); + process_relay_message(tls, relay_url, text); } free(text); } -- cgit v1.2.3