From 50efddd5f06cfadb48347e1f859723926cea2194 Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 6 Oct 2026 19:29:17 +0200 Subject: [PATCH] Allow immutable avatar originals and manifests in storage --- src/member_files/protocol.c | 12 +++++++++++- src/member_files/protocol.h | 4 ++++ src/member_files/storage.c | 5 ++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/member_files/protocol.c b/src/member_files/protocol.c index 50d2f0f5..6163ad4f 100644 --- a/src/member_files/protocol.c +++ b/src/member_files/protocol.c @@ -4,11 +4,21 @@ #include int mf_avatar_file_valid(const char* file) { - if (!file || strlen(file) != 68 || strcmp(file + 64, ".png")) return 0; + if (!file || strlen(file) < 68 || strlen(file) > 69) return 0; + const char* suffix = file + 64; + if (strcmp(suffix, ".png") && strcmp(suffix, ".jpg") && strcmp(suffix, ".webp") && strcmp(suffix, ".json")) return 0; for (unsigned i = 0; i < 64; i++) if (!((file[i] >= '0' && file[i] <= '9') || (file[i] >= 'a' && file[i] <= 'f'))) return 0; return 1; } +int mf_avatar_manifest_valid(const char* file) { return mf_avatar_file_valid(file) && !strcmp(file + 64, ".json"); } + +/* Раздельные пределы метаданных и изображений; фактический хеш проверяет COMMIT. */ +uint64_t mf_avatar_file_limit(const char* file) { + if (!mf_avatar_file_valid(file)) return 0; + return mf_avatar_manifest_valid(file) ? MF_AVATAR_MANIFEST_MAX_BYTES : MF_AVATAR_ORIGINAL_MAX_BYTES; +} + uint64_t mf_get64(const uint8_t* p) { uint64_t v = 0; for (unsigned i = 0; i < 8; i++) v |= (uint64_t)p[i] << (8 * i); diff --git a/src/member_files/protocol.h b/src/member_files/protocol.h index 77e021dc..743a760c 100644 --- a/src/member_files/protocol.h +++ b/src/member_files/protocol.h @@ -23,7 +23,11 @@ extern "C" { #define MF_ENTRY_HEADER 35 #define MF_QUOTA_BYTES 40 /* used/limit/reserved в байтах, files/directories; пять u64 LE */ #define MF_AVATAR_MAX_BYTES (1024 * 1024) +#define MF_AVATAR_ORIGINAL_MAX_BYTES (32 * 1024 * 1024) +#define MF_AVATAR_MANIFEST_MAX_BYTES 4096 int mf_avatar_file_valid(const char* file); +int mf_avatar_manifest_valid(const char* file); +uint64_t mf_avatar_file_limit(const char* file); #define MF_IDLE_TB 100000ULL /* 10 секунд */ #define MF_REQUEST 1 #define MF_RESPONSE 2 diff --git a/src/member_files/storage.c b/src/member_files/storage.c index ac73f1a3..fc373d25 100644 --- a/src/member_files/storage.c +++ b/src/member_files/storage.c @@ -326,7 +326,10 @@ static struct store_upload* store_upload_find(struct mf_storage* s, uint64_t gro static int store_begin(struct mf_storage* s, uint64_t group, uint64_t peer, const char* path, const struct mf_message* q) { if (q->size > INT64_MAX || mf_zero(q->upload, 16)) return MF_INVALID_RANGE; if (!strncmp(q->path, "avatars/", 8)) { - if (!mf_avatar_file_valid(q->path + 8) || !q->size || q->size > MF_AVATAR_MAX_BYTES) return MF_INVALID_RANGE; + if (!q->size || q->size > mf_avatar_file_limit(q->path + 8)) { + DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "avatar: invalid object path=%s bytes=%llu", q->path, (unsigned long long)q->size); + return MF_INVALID_RANGE; + } if (!mf_zero(q->version, 16)) return MF_EXISTS; char hash[65]; for (unsigned i = 0; i < 32; i++) snprintf(hash + i * 2, 3, "%02x", q->hash[i]); if (strncmp(q->path + 8, hash, 64)) return MF_HASH_MISMATCH;