v0.0.14 - Handle malformed nostr_post arguments with loose JSON fallback parsing

This commit is contained in:
Your Name
2026-02-28 17:52:20 -04:00
parent 6e74ef5ac6
commit 56b9ae421c
3 changed files with 183 additions and 14 deletions
+2 -2
View File
@@ -12,11 +12,11 @@ Because all identity, communication, and memory live on Nostr, the agent is **po
**Skills are the new apps.** Agents learn capabilities through skills — public Nostr events that any agent can discover, adopt, and share. There is no app store, no gatekeeper, no approval process. If someone publishes a useful skill, your agent can find it through your web of trust and start using it. Popularity is measured by adoption, not by a rating algorithm. The best skills spread because agents actually use them.
## Current Status — v0.0.13
## Current Status — v0.0.14
**Active build — this project is barely working. Experiment at your own risk.**
> Last release update: v0.0.13 — Enrich nostr_post responses with event ID, note URI, and relay publish metadata
> Last release update: v0.0.14 — Handle malformed nostr_post arguments with loose JSON fallback parsing
- Connects to configured relays with auto-reconnect and relay state transition logging
- Publishes configured startup events per relay as each relay becomes connected
+2 -2
View File
@@ -12,8 +12,8 @@
// Using DIDACTYL_ prefix to avoid conflicts with nostr_core_lib VERSION macros
#define DIDACTYL_VERSION_MAJOR 0
#define DIDACTYL_VERSION_MINOR 0
#define DIDACTYL_VERSION_PATCH 13
#define DIDACTYL_VERSION "v0.0.13"
#define DIDACTYL_VERSION_PATCH 14
#define DIDACTYL_VERSION "v0.0.14"
// Agent metadata
#define DIDACTYL_NAME "Didactyl"
+179 -10
View File
@@ -6,6 +6,7 @@
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <ctype.h>
#include "cjson/cJSON.h"
#include "nostr_handler.h"
@@ -20,16 +21,6 @@ static char* json_error(const char* msg) {
return out;
}
static char* json_success_with_message(const char* msg) {
cJSON* root = cJSON_CreateObject();
if (!root) return NULL;
cJSON_AddBoolToObject(root, "success", 1);
cJSON_AddStringToObject(root, "message", msg ? msg : "ok");
char* out = cJSON_PrintUnformatted(root);
cJSON_Delete(root);
return out;
}
static char* sanitize_json_string_controls(const char* in) {
if (!in) return NULL;
@@ -96,6 +87,181 @@ static char* sanitize_json_string_controls(const char* in) {
return out;
}
static const char* find_key_start(const char* in, const char* key) {
if (!in || !key) return NULL;
char pattern[64];
int n = snprintf(pattern, sizeof(pattern), "\"%s\"", key);
if (n <= 0 || (size_t)n >= sizeof(pattern)) return NULL;
return strstr(in, pattern);
}
static const char* skip_ws(const char* p) {
while (p && *p && isspace((unsigned char)*p)) p++;
return p;
}
static int parse_loose_kind(const char* in, int* out_kind) {
if (!in || !out_kind) return -1;
const char* p = find_key_start(in, "kind");
if (!p) return -1;
p = strchr(p, ':');
if (!p) return -1;
p = skip_ws(p + 1);
if (!p || !*p) return -1;
char* end = NULL;
long v = strtol(p, &end, 10);
if (end == p) return -1;
*out_kind = (int)v;
return 0;
}
static char* parse_loose_json_string_value(const char* in, const char* key) {
if (!in || !key) return NULL;
const char* p = find_key_start(in, key);
if (!p) return NULL;
p = strchr(p, ':');
if (!p) return NULL;
p = skip_ws(p + 1);
if (!p || *p != '"') return NULL;
p++;
size_t max_len = strlen(p);
char* out = (char*)malloc(max_len + 1U);
if (!out) return NULL;
size_t j = 0;
int escaping = 0;
for (size_t i = 0; p[i] != '\0'; i++) {
char c = p[i];
if (escaping) {
switch (c) {
case 'n': out[j++] = '\n'; break;
case 'r': out[j++] = '\r'; break;
case 't': out[j++] = '\t'; break;
case '"': out[j++] = '"'; break;
case '\\': out[j++] = '\\'; break;
default: out[j++] = c; break;
}
escaping = 0;
continue;
}
if (c == '\\') {
escaping = 1;
continue;
}
if (c == '"') {
out[j] = '\0';
return out;
}
out[j++] = c;
}
out[j] = '\0';
return out;
}
static cJSON* parse_loose_tags_array(const char* in) {
if (!in) return NULL;
const char* p = find_key_start(in, "tags");
if (!p) return NULL;
p = strchr(p, ':');
if (!p) return NULL;
p = skip_ws(p + 1);
if (!p || *p != '[') return NULL;
const char* start = p;
int depth = 0;
int in_string = 0;
int escaping = 0;
for (; *p; p++) {
char c = *p;
if (escaping) {
escaping = 0;
continue;
}
if (c == '\\' && in_string) {
escaping = 1;
continue;
}
if (c == '"') {
in_string = !in_string;
continue;
}
if (in_string) {
continue;
}
if (c == '[') depth++;
else if (c == ']') {
depth--;
if (depth == 0) {
size_t len = (size_t)(p - start + 1);
char* arr_json = (char*)malloc(len + 1U);
if (!arr_json) return NULL;
memcpy(arr_json, start, len);
arr_json[len] = '\0';
cJSON* tags = cJSON_Parse(arr_json);
free(arr_json);
if (tags && cJSON_IsArray(tags)) {
return tags;
}
cJSON_Delete(tags);
return NULL;
}
}
}
return NULL;
}
static cJSON* parse_loose_nostr_post_args(const char* in) {
if (!in) return NULL;
int kind = 0;
if (parse_loose_kind(in, &kind) != 0) {
return NULL;
}
char* content = parse_loose_json_string_value(in, "content");
if (!content) {
return NULL;
}
cJSON* args = cJSON_CreateObject();
if (!args) {
free(content);
return NULL;
}
cJSON_AddNumberToObject(args, "kind", kind);
cJSON_AddStringToObject(args, "content", content);
free(content);
cJSON* tags = parse_loose_tags_array(in);
if (tags) {
cJSON_AddItemToObject(args, "tags", tags);
}
return args;
}
static int is_safe_relative_path(const char* path) {
if (!path || path[0] == '\0') return 0;
if (path[0] == '/') return 0;
@@ -292,6 +458,9 @@ static char* execute_nostr_post(const char* args_json) {
args = cJSON_Parse(repaired_args_json);
}
}
if (!args && args_json) {
args = parse_loose_nostr_post_args(args_json);
}
if (!args) {
free(repaired_args_json);
return json_error("invalid arguments JSON");