From 08cc4f9d7571a4294462a80203fa9be5fcb0c7ba Mon Sep 17 00:00:00 2001 From: synt-xerror <169557594+synt-xerror@users.noreply.github.com> Date: Mon, 16 Feb 2026 19:08:31 -0300 Subject: [PATCH] Security improvements --- src/neocities.c | 386 +++++++++++++++++++++++++++++------------------- 1 file changed, 231 insertions(+), 155 deletions(-) diff --git a/src/neocities.c b/src/neocities.c index 5d430a5..5003a1f 100644 --- a/src/neocities.c +++ b/src/neocities.c @@ -5,222 +5,298 @@ #include #include -// ------------------------- + // ------------------------- // HTTP responde buffer // ------------------------- struct response { - char *data; - size_t len; -}; + char *data; + size_t len; +}; -static size_t write_cb(void *ptr, size_t size, size_t nmemb, void *userdata) { - struct response *r = (struct response*)userdata; - size_t chunk = size * nmemb; - char *tmp = realloc(r->data, r->len + chunk + 1); - if (!tmp) return 0; - r->data = tmp; - memcpy(r->data + r->len, ptr, chunk); - r->len += chunk; - r->data[r->len] = '\0'; - return chunk; +static size_t write_cb( + void *ptr, + size_t size, + size_t nmemb, + void *userdata +) +{ + struct response *r = (struct response*)userdata; + size_t chunk = size * nmemb; + char *tmp = realloc(r->data, r->len + chunk + 1); + if (!tmp) return 0; + r->data = tmp; + memcpy(r->data + r->len, ptr, chunk); + r->len += chunk; + r->data[r->len] = '\0'; + return chunk; } // ------------------------- // HTTP helpers // ------------------------- -static int perform_request(const char *url, const char *user, const char *pass, - const char *post_fields, struct response *resp) +static int perform_request( + const char *url, + const char *api_key, + const char *post_fields, + struct response *resp, + curl_mime *mime +) { - CURL *curl = curl_easy_init(); - if (!curl) return 1; - resp->data = malloc(1); - resp->len = 0; + CURL *curl = curl_easy_init(); + if (!curl) return 1; - curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, resp); + resp->data = malloc(1); + resp->len = 0; + resp->data[0] = '\0'; - if (user && pass) { - char auth[256]; - snprintf(auth, sizeof(auth), "%s:%s", user, pass); - curl_easy_setopt(curl, CURLOPT_USERPWD, auth); - } + struct curl_slist *headers = NULL; + + if (api_key && strlen(api_key) > 0) { + char auth_header[512]; + snprintf(auth_header, sizeof(auth_header), + "Authorization: Bearer %s", api_key); + headers = curl_slist_append(headers, auth_header); + } - if (post_fields) { - curl_easy_setopt(curl, CURLOPT_POST, 1L); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_fields); - } + curl_easy_setopt(curl, CURLOPT_URL, url); + + if (headers) + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + + /* SSL hardening */ + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); + + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 0L); // política + + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, 60L); - CURLcode res = curl_easy_perform(curl); - curl_easy_cleanup(curl); - if (res != CURLE_OK) { - free(resp->data); - return 2; - } - return 0; + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, resp); + + if (post_fields) { + curl_easy_setopt(curl, CURLOPT_POST, 1L); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_fields); + } + + if (mime) + curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime); + + CURLcode res = curl_easy_perform(curl); + + curl_slist_free_all(headers); + curl_easy_cleanup(curl); + + long http_code = 0; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); + + if (res != CURLE_OK || http_code >= 400) { + curl_slist_free_all(headers); + curl_easy_cleanup(curl); + free(resp->data); + return 2; + } + + return 0; } // ------------------------- // JSON parsers // ------------------------- -void neocities_free_info(neocities_info_t *info) { - if (!info) return; - free(info->sitename); - free(info->created_at); - free(info->last_updated); - free(info->domain); - for (size_t i=0; itag_count; i++) free(info->tags[i]); - free(info->tags); +void neocities_free_info( + neocities_info_t *info +) +{ + if (!info) return; + free(info->sitename); + free(info->created_at); + free(info->last_updated); + free(info->domain); + for (size_t i=0; itag_count; i++) free(info->tags[i]); + free(info->tags); } -void neocities_free_filelist(neocities_filelist_t *list) { - if (!list) return; - for (size_t i=0; icount; i++) free(list->paths[i]); - free(list->paths); +void neocities_free_filelist( + neocities_filelist_t *list +) +{ + if (!list) return; + for (size_t i=0; icount; i++) free(list->paths[i]); + free(list->paths); } // ------------------------- // info — GET /api/info // ------------------------- -int neocities_info(const char *sitename, neocities_info_t *out) { - char url[512]; - if (sitename) - snprintf(url, sizeof(url), "https://neocities.org/api/info?sitename=%s", sitename); - else - snprintf(url, sizeof(url), "https://neocities.org/api/info"); +int neocities_info( + const char *sitename, neocities_info_t *out +) +{ + char url[512]; + if (sitename) + snprintf(url, sizeof(url), "https://neocities.org/api/info?sitename=%s", sitename); + else + snprintf(url, sizeof(url), "https://neocities.org/api/info"); - struct response resp; - int ret = perform_request(url, NULL, NULL, NULL, &resp); - if (ret) return ret; + struct response resp; + int ret = perform_request(url, NULL, NULL, &resp, NULL); + if (ret) return ret; - json_error_t error; - json_t *root = json_loads(resp.data, 0, &error); - free(resp.data); - if (!root) return 3; + json_error_t error; + json_t *root = json_loads(resp.data, 0, &error); + free(resp.data); + if (!root) return 3; - const char *result = json_string_value(json_object_get(root,"result")); - if (!result || strcmp(result,"success")!=0) { json_decref(root); return 4; } + const char *result = json_string_value(json_object_get(root,"result")); + if (!result || strcmp(result,"success")!=0) { json_decref(root); return 4; } - json_t *info = json_object_get(root,"info"); - out->sitename = strdup(json_string_value(json_object_get(info,"sitename"))); - out->hits = (int)json_integer_value(json_object_get(info,"hits")); - out->created_at = strdup(json_string_value(json_object_get(info,"created_at"))); - out->last_updated= strdup(json_string_value(json_object_get(info,"last_updated"))); - out->domain = strdup(json_string_value(json_object_get(info,"domain"))); + json_t *info = json_object_get(root,"info"); + out->sitename = strdup(json_string_value(json_object_get(info,"sitename"))); + out->hits = (int)json_integer_value(json_object_get(info,"hits")); + out->created_at = strdup(json_string_value(json_object_get(info,"created_at"))); + out->last_updated= strdup(json_string_value(json_object_get(info,"last_updated"))); + out->domain = strdup(json_string_value(json_object_get(info,"domain"))); - json_t *tags = json_object_get(info,"tags"); - size_t tc = json_array_size(tags); - out->tags = malloc(sizeof(char*) * tc); - out->tag_count = tc; - for (size_t i=0; itags[i] = strdup(json_string_value(json_array_get(tags,i))); + json_t *tags = json_object_get(info,"tags"); + size_t tc = json_array_size(tags); + out->tags = malloc(sizeof(char*) * tc); + out->tag_count = tc; + for (size_t i=0; itags[i] = strdup(json_string_value(json_array_get(tags,i))); - json_decref(root); - return 0; + json_decref(root); + return 0; } // ------------------------- // list — GET /api/list // ------------------------- -int neocities_list(const char *user, const char *pass, const char *path, neocities_filelist_t *out) { - char url[512]; - if (path) snprintf(url, sizeof(url), "https://neocities.org/api/list?path=%s", path); - else snprintf(url, sizeof(url), "https://neocities.org/api/list"); +int neocities_list( + const char *api_key, + const char *path, neocities_filelist_t *out +) +{ + char url[512]; + if (path) snprintf(url, sizeof(url), "https://neocities.org/api/list?path=%s", path); + else snprintf(url, sizeof(url), "https://neocities.org/api/list"); - struct response resp; - int ret = perform_request(url, user, pass, NULL, &resp); - if (ret) return ret; + struct response resp; + int ret = perform_request(url, api_key, NULL, &resp, NULL); + if (ret) return ret; - json_error_t error; - json_t *root = json_loads(resp.data, 0, &error); - free(resp.data); - if (!root) return 3; + json_error_t error; + json_t *root = json_loads(resp.data, 0, &error); + free(resp.data); + if (!root) return 3; - json_t *arr = json_object_get(root, "files"); - if (!arr) { json_decref(root); return 4; } + json_t *arr = json_object_get(root, "files"); + if (!arr) { json_decref(root); return 4; } - size_t count = json_array_size(arr); - out->paths = malloc(sizeof(char*) * count); - out->count = count; - for (size_t i=0; ipaths[i] = strdup(json_string_value(json_object_get(f,"path"))); - } - json_decref(root); - return 0; + size_t count = json_array_size(arr); + out->paths = malloc(sizeof(char*) * count); + out->count = count; + for (size_t i=0; ipaths[i] = strdup(json_string_value(json_object_get(f,"path"))); + } + json_decref(root); + return 0; } // ------------------------- // delete — POST /api/delete // ------------------------- -int neocities_delete(const char *user, const char *pass, const char **filenames, size_t count, char **response) { - char body[1024] = ""; - for (size_t i=0; i