v0.0.14 - Handle malformed nostr_post arguments with loose JSON fallback parsing
This commit is contained in:
@@ -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
@@ -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
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user