From: Vsevolod Stakhov Date: Tue, 23 Sep 2025 10:51:20 +0000 (+0100) Subject: [Minor] Add tests for encrypted zip support + fix some memory issues X-Git-Tag: 3.13.1~16^2~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f2cfd9f8ae4547495b299d259e5fde979c53d41e;p=thirdparty%2Frspamd.git [Minor] Add tests for encrypted zip support + fix some memory issues --- diff --git a/src/lua/lua_archive.c b/src/lua/lua_archive.c index 9bddb6ed3d..fa67d60617 100644 --- a/src/lua/lua_archive.c +++ b/src/lua/lua_archive.c @@ -202,8 +202,14 @@ lua_archive_zip(lua_State *L) return lua_archive_pack(L); } -/** - * zip_encrypt(files, password) -> text +/*** + * @function archive.zip_encrypt(files[, password]) + * Create a ZIP archive in-memory using Rspamd ZIP writer. + * If password is provided and non-empty, entries are encrypted via WinZip AES (AE-2). + * - AES-256-CTR with HMAC-SHA1 (10-byte tag), interoperable with 7-Zip/WinZip/libarchive + * @param {table} files array: { name = string, content = string|rspamd_text, [mode|perms] = int, [mtime] = int } + * @param {string} password optional password string + * @return {text} archive bytes */ static int lua_archive_zip_encrypt(lua_State *L) @@ -529,9 +535,11 @@ lua_archive_pack(lua_State *L) wctx.buf = g_byte_array_new(); if (archive_write_open(a, &wctx, lua_archive_write_open, lua_archive_write_cb, lua_archive_write_close) != ARCHIVE_OK) { + const char *aerr = archive_error_string(a); + lua_pushfstring(L, "cannot open archive writer: %s", aerr ? aerr : "unknown error"); g_byte_array_free(wctx.buf, TRUE); archive_write_free(a); - return luaL_error(L, "cannot open archive writer: %s", archive_error_string(a)); + return lua_error(L); } /* Iterate files table */ @@ -556,21 +564,23 @@ lua_archive_pack(lua_State *L) int r = archive_write_header(a, ae); if (r != ARCHIVE_OK) { - const char *err = archive_error_string(a); + const char *aerr = archive_error_string(a); + lua_pushfstring(L, "cannot write header: %s", aerr ? aerr : "unknown error"); archive_entry_free(ae); archive_write_free(a); g_byte_array_free(wctx.buf, TRUE); - return luaL_error(L, "cannot write header: %s", err ? err : "unknown error"); + return lua_error(L); } if (dlen > 0) { la_ssize_t wr = archive_write_data(a, data, dlen); if (wr < 0 || (size_t) wr != dlen) { - const char *err = archive_error_string(a); + const char *aerr = archive_error_string(a); + lua_pushfstring(L, "cannot write data: %s", aerr ? aerr : "unknown error"); archive_entry_free(ae); archive_write_free(a); g_byte_array_free(wctx.buf, TRUE); - return luaL_error(L, "cannot write data: %s", err ? err : "unknown error"); + return lua_error(L); } } @@ -631,9 +641,10 @@ lua_archive_unpack(lua_State *L) int r = archive_read_open_memory(a, t->start, t->len); if (r != ARCHIVE_OK) { - const char *err = archive_error_string(a); + const char *aerr = archive_error_string(a); + lua_pushfstring(L, "cannot open archive: %s", aerr ? aerr : "unknown error"); archive_read_free(a); - return luaL_error(L, "cannot open archive: %s", err ? err : "unknown error"); + return lua_error(L); } lua_newtable(L); @@ -659,10 +670,11 @@ lua_archive_unpack(lua_State *L) break; } else if (rr < 0) { - const char *err = archive_error_string(a); + const char *aerr = archive_error_string(a); + lua_pushfstring(L, "cannot read data: %s", aerr ? aerr : "unknown error"); g_byte_array_free(ba, TRUE); archive_read_free(a); - return luaL_error(L, "cannot read data: %s", err ? err : "unknown error"); + return lua_error(L); } g_byte_array_append(ba, (const guint8 *) buf, (guint) rr); } diff --git a/test/lua/unit/archive.lua b/test/lua/unit/archive.lua index 5c38c40050..fe194322c9 100644 --- a/test/lua/unit/archive.lua +++ b/test/lua/unit/archive.lua @@ -32,6 +32,35 @@ context("Lua archive bindings", function() assert_rspamd_eq({ actual = out[1].content, expect = rnd }) end) + test("zip_encrypt without password == plain zip", function() + local files = { + { name = "a.txt", content = "Hello" }, + } + local blob = archive.zip_encrypt(files) -- no password + assert_equal(type(blob), "userdata") + local out = archive.unzip(blob) + assert_equal(#out, 1) + assert_equal(out[1].name, "a.txt") + assert_rspamd_eq({ actual = out[1].content, expect = rspamd_text.fromstring("Hello") }) + end) + + test("zip_encrypt with password (AE-2) roundtrip via libarchive", function() + local files = { + { name = "dir/x.txt", content = "secret" }, + { name = "y.bin", content = rspamd_text.fromstring("\001\002\003") }, + } + local pwd = "testpass123" + local blob = archive.zip_encrypt(files, pwd) + assert_equal(type(blob), "userdata") + -- libarchive can read AE-2, so unpack should succeed and yield the same files + local out = archive.unpack(blob, "zip") + assert_equal(#out, 2) + local names = {} + for _, f in ipairs(out) do names[f.name] = f.content end + assert_rspamd_eq({ actual = names["dir/x.txt"], expect = rspamd_text.fromstring("secret") }) + assert_rspamd_eq({ actual = names["y.bin"], expect = rspamd_text.fromstring("\001\002\003") }) + end) + test("tar/untar helpers roundtrip (no compression)", function() local files = { { name = "x.txt", content = "X" },