Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package apk-tools for openSUSE:Factory 
checked in at 2026-07-31 15:30:39
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/apk-tools (Old)
 and      /work/SRC/openSUSE:Factory/.apk-tools.new.2004 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "apk-tools"

Fri Jul 31 15:30:39 2026 rev:8 rq:1368481 version:3.0.7

Changes:
--------
--- /work/SRC/openSUSE:Factory/apk-tools/apk-tools.changes      2026-04-13 
23:22:32.569176698 +0200
+++ /work/SRC/openSUSE:Factory/.apk-tools.new.2004/apk-tools.changes    
2026-07-31 16:05:50.709376250 +0200
@@ -1,0 +2,11 @@
+Wed Jul 29 07:43:26 UTC 2026 - Martin Hauke <[email protected]>
+
+- Update to version 3.0.7:
+  * extract: harden validation of malicious filenames during
+    extraction.
+  * genhelp: add Zsh completion generator.
+  * repoparser: allow lines starting with variable.
+  * genhelp: make gzipped embedded help reproducible.
+  * doc: misc improvements.
+
+-------------------------------------------------------------------

Old:
----
  apk-tools-3.0.6.tar.xz

New:
----
  apk-tools-3.0.7.tar.xz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ apk-tools.spec ++++++
--- /var/tmp/diff_new_pack.THSS6Y/_old  2026-07-31 16:05:51.161391978 +0200
+++ /var/tmp/diff_new_pack.THSS6Y/_new  2026-07-31 16:05:51.165392117 +0200
@@ -21,7 +21,7 @@
 %define soname 3_0_0
 %define libname libapk%{soname}
 Name:           apk-tools
-Version:        3.0.6
+Version:        3.0.7
 Release:        0
 Summary:        Alpine package manager
 License:        GPL-2.0-or-later
@@ -98,6 +98,7 @@
 %{_mandir}/man8/apk.8%{?ext_man}
 %{_mandir}/man[5,8]/apk-*.[5,8]%{?ext_man}
 %{_datadir}/bash-completion/completions/_apk
+%{_datadir}/zsh/site-functions/_apk_zsh
 %{python_sitearch}/apk.cpython-*.so
 
 %files -n %{libname}

++++++ _service ++++++
--- /var/tmp/diff_new_pack.THSS6Y/_old  2026-07-31 16:05:51.201393370 +0200
+++ /var/tmp/diff_new_pack.THSS6Y/_new  2026-07-31 16:05:51.205393509 +0200
@@ -2,7 +2,7 @@
   <service name="tar_scm" mode="manual">
     <param name="url">https://git.alpinelinux.org/apk-tools</param>
     <param name="scm">git</param>
-    <param name="revision">v3.0.6</param>
+    <param name="revision">v3.0.7</param>
     <param name="changesgenerate">enable</param>
     <param name="versionrewrite-pattern">v(.*)</param>
     <param name="versionformat">@PARENT_TAG@</param>

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.THSS6Y/_old  2026-07-31 16:05:51.225394205 +0200
+++ /var/tmp/diff_new_pack.THSS6Y/_new  2026-07-31 16:05:51.233394483 +0200
@@ -1,6 +1,6 @@
 <servicedata>
 <service name="tar_scm">
                 <param name="url">https://git.alpinelinux.org/apk-tools</param>
-              <param 
name="changesrevision">7e92634afe99db29e00037e6a134104ad9720b68</param></service></servicedata>
+              <param 
name="changesrevision">522171e05c72c8a0dd2a9392f053c89196fbdb9b</param></service></servicedata>
 (No newline at EOF)
 

++++++ apk-tools-3.0.6.tar.xz -> apk-tools-3.0.7.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apk-tools-3.0.6/VERSION new/apk-tools-3.0.7/VERSION
--- old/apk-tools-3.0.6/VERSION 2026-04-13 10:36:44.000000000 +0200
+++ new/apk-tools-3.0.7/VERSION 2026-07-28 13:28:06.000000000 +0200
@@ -1 +1 @@
-3.0.6
+3.0.7
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apk-tools-3.0.6/doc/apk-protected_paths.5.scd 
new/apk-tools-3.0.7/doc/apk-protected_paths.5.scd
--- old/apk-tools-3.0.6/doc/apk-protected_paths.5.scd   2026-04-13 
10:36:44.000000000 +0200
+++ new/apk-tools-3.0.7/doc/apk-protected_paths.5.scd   2026-07-28 
13:28:06.000000000 +0200
@@ -47,13 +47,25 @@
 
 Lines starting with *#* and empty lines are ignored.
 
-# EXAMPLES
+# DEFAULTS
+
+Even if _/etc/apk/protected_paths.d/\*.list_  is empty, the following is used 
by
+default:
 
 ```
-# This line is ignored; it is a comment.
 +etc/
 @etc/init.d
 !etc/apk
+```
+
+The contents of _/etc/apk/protected_paths.d/\*.list_ are applied after the
+above defaults. When a flag overrides the _protected_paths.d_ directory, it 
also
+overrides these defaults.
+
+# MORE EXAMPLES
+
+```
+# This line is ignored; it is a comment.
 \-etc/ssl/certs/ca-cert-\*.pem
 ```
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apk-tools-3.0.6/doc/apk-query.8.scd 
new/apk-tools-3.0.7/doc/apk-query.8.scd
--- old/apk-tools-3.0.6/doc/apk-query.8.scd     2026-04-13 10:36:44.000000000 
+0200
+++ new/apk-tools-3.0.7/doc/apk-query.8.scd     2026-07-28 13:28:06.000000000 
+0200
@@ -134,4 +134,4 @@
 apk query --match name,provides cmd:apk --fields origin
 
 \# print source packages with specific dependency name++
-apk query --match dependency so:libapk.so.2.14.9 --fields origin
+apk query --match depends so:libapk.so.2.14.9 --fields origin
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apk-tools-3.0.6/doc/apk-repositories.5.scd 
new/apk-tools-3.0.7/doc/apk-repositories.5.scd
--- old/apk-tools-3.0.6/doc/apk-repositories.5.scd      2026-04-13 
10:36:44.000000000 +0200
+++ new/apk-tools-3.0.7/doc/apk-repositories.5.scd      2026-07-28 
13:28:06.000000000 +0200
@@ -40,10 +40,9 @@
 
 *ndx \[@tag\] url*++
 *\[v2|v3\] \[@tag\] url \[component...\]*
-       If a repository declaration omits the type field, it defaults to the 
type
-       defined in latest *default* command, or if absent, to *v2* (or *ndx* if 
*url*
-       ends with *.adb* or *.tar.gz*). If the format is *v3* or the *ndx* 
filename
-       ends with *.adb* a v3 format index file required.
+       If a repository declaration omits the type field, it defaults to *v2*
+       (or *ndx* if *url* ends with *.adb* or *.tar.gz*). If the format is *v3*
+       or the *ndx* filename ends with *.adb* a v3 format index file required.
 
        The optional *@tag* can be present to indicate that the repository 
should
        not be used by default. Refer to *apk-world*(5) on how to enable 
installation
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apk-tools-3.0.6/doc/apk.8.scd 
new/apk-tools-3.0.7/doc/apk.8.scd
--- old/apk-tools-3.0.6/doc/apk.8.scd   2026-04-13 10:36:44.000000000 +0200
+++ new/apk-tools-3.0.7/doc/apk.8.scd   2026-07-28 13:28:06.000000000 +0200
@@ -337,7 +337,7 @@
 
 The following options are available for all commands which generate APKv3 
files.
 
-*--compression, -C* _ALGORITHM[:LEVEL]_
+*--compression*, *-c* _ALGORITHM[:LEVEL]_
        Compress the file with given _ALGORITHM_ and _LEVEL_. Supported 
algorithms:
        - none
        - deflate (level 1-9)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apk-tools-3.0.6/doc/meson.build 
new/apk-tools-3.0.7/doc/meson.build
--- old/apk-tools-3.0.6/doc/meson.build 2026-04-13 10:36:44.000000000 +0200
+++ new/apk-tools-3.0.7/doc/meson.build 2026-07-28 13:28:06.000000000 +0200
@@ -1,5 +1,6 @@
 man_filenames = [
     'apk.8.scd',
+    'apk-cache.5.scd',
     'apk-keys.5.scd',
     'apk-package.5.scd',
     'apk-protected_paths.5.scd',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apk-tools-3.0.6/src/apk_fs.h 
new/apk-tools-3.0.7/src/apk_fs.h
--- old/apk-tools-3.0.6/src/apk_fs.h    2026-04-13 10:36:44.000000000 +0200
+++ new/apk-tools-3.0.7/src/apk_fs.h    2026-07-28 13:28:06.000000000 +0200
@@ -51,6 +51,9 @@
 #define APK_FSEXTRACTF_NO_SYS_XATTRS   0x0004
 #define APK_FSEXTRACTF_NO_DEVICES      0x0008
 
+bool apk_fs_is_malicious_filename(apk_blob_t file);
+bool apk_fs_is_malicious_pathname(apk_blob_t path);
+
 int apk_fs_extract(struct apk_ctx *, const struct apk_file_info *, struct 
apk_istream *, unsigned int, apk_blob_t);
 
 void apk_fsdir_get(struct apk_fsdir *, apk_blob_t dir, unsigned int 
extract_flags, struct apk_ctx *ac, apk_blob_t pkgctx);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apk-tools-3.0.6/src/app_mkpkg.c 
new/apk-tools-3.0.7/src/app_mkpkg.c
--- old/apk-tools-3.0.6/src/app_mkpkg.c 2026-04-13 10:36:44.000000000 +0200
+++ new/apk-tools-3.0.7/src/app_mkpkg.c 2026-07-28 13:28:06.000000000 +0200
@@ -24,6 +24,7 @@
 #include "apk_balloc.h"
 #include "apk_print.h"
 #include "apk_xattr.h"
+#include "apk_fs.h"
 
 struct mkpkg_hardlink_key {
        dev_t device;
@@ -224,6 +225,8 @@
        if (r) return r;
        if (!S_ISDIR(fi.mode)) return 0;
 
+       if (apk_fs_is_malicious_filename(APK_BLOB_STR(entry))) return 
-APKE_ADB_SCHEMA;
+
        int n = apk_pathbuilder_push(&ctx->pb, entry);
        apk_string_array_add(&ctx->pathnames, apk_balloc_cstr(&ctx->ba, 
apk_pathbuilder_get(&ctx->pb)));
        r = apk_dir_foreach_file_sorted(dirfd, entry, mkpkg_scan_dirent, ctx, 
NULL);
@@ -270,6 +273,8 @@
        } ft;
        int r, n;
 
+       if (apk_fs_is_malicious_filename(APK_BLOB_STR(entry))) return 
-APKE_ADB_SCHEMA;
+
        ctx->num_dirents++;
        r = apk_fileinfo_get(dirfd, entry, APK_FI_NOFOLLOW | 
APK_FI_DIGEST(APK_DIGEST_SHA256), &fi, NULL);
        if (r) return r;
@@ -382,7 +387,7 @@
        adb_wo_obj(&fio, ADBI_DI_FILES, &ctx->files);
        adb_wa_append_obj(&ctx->paths, &fio);
 done:
-       if (r) apk_err(out, "failed to process directory '%s': %d", 
apk_pathbuilder_cstr(&ctx->pb), r);
+       if (r) apk_err(out, "failed to process directory '%s': %s", entry, 
apk_error_str(r));
        return r;
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apk-tools-3.0.6/src/database.c 
new/apk-tools-3.0.7/src/database.c
--- old/apk-tools-3.0.6/src/database.c  2026-04-13 10:36:44.000000000 +0200
+++ new/apk-tools-3.0.7/src/database.c  2026-07-28 13:28:06.000000000 +0200
@@ -2727,14 +2727,6 @@
        return 0;
 }
 
-static int contains_control_character(const char *str)
-{
-       for (const uint8_t *p = (const uint8_t *) str; *p; p++) {
-               if (*p < 0x20 || *p == 0x7f) return 1;
-       }
-       return 0;
-}
-
 static int apk_db_install_v2meta(struct apk_extract_ctx *ectx, struct 
apk_istream *is)
 {
        struct install_ctx *ctx = container_of(ectx, struct install_ctx, ectx);
@@ -2804,7 +2796,6 @@
 static int apk_db_install_file(struct apk_extract_ctx *ectx, const struct 
apk_file_info *ae, struct apk_istream *is)
 {
        struct install_ctx *ctx = container_of(ectx, struct install_ctx, ectx);
-       static const char dot1[] = "/./", dot2[] = "/../";
        struct apk_database *db = ctx->db;
        struct apk_ctx *ac = db->ctx;
        struct apk_out *out = &ac->out;
@@ -2817,17 +2808,6 @@
 
        apk_db_run_pending_script(ctx);
 
-       /* Sanity check the file name */
-       if (ae->name[0] == '/' || contains_control_character(ae->name) ||
-           strncmp(ae->name, &dot1[1], 2) == 0 ||
-           strncmp(ae->name, &dot2[1], 3) == 0 ||
-           strstr(ae->name, dot1) || strstr(ae->name, dot2)) {
-               apk_warn(out, PKG_VER_FMT": ignoring malicious file %s",
-                       PKG_VER_PRINTF(pkg), ae->name);
-               ipkg->broken_files = 1;
-               return 0;
-       }
-
        /* Installable entry */
        if (!S_ISDIR(ae->mode)) {
                if (!apk_blob_rsplit(name, '/', &bdir, &bfile)) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apk-tools-3.0.6/src/extract_v3.c 
new/apk-tools-3.0.7/src/extract_v3.c
--- old/apk-tools-3.0.6/src/extract_v3.c        2026-04-13 10:36:44.000000000 
+0200
+++ new/apk-tools-3.0.7/src/extract_v3.c        2026-07-28 13:28:06.000000000 
+0200
@@ -13,6 +13,7 @@
 #include "apk_extract.h"
 #include "apk_adb.h"
 #include "apk_pathbuilder.h"
+#include "apk_fs.h"
 
 struct apk_extract_v3_ctx {
        struct apk_extract_ctx *ectx;
@@ -137,6 +138,26 @@
        return r;
 }
 
+static int apk_extract_v3_validate_filetree(struct adb_obj *pkg)
+{
+       struct adb_obj paths, path, files, file;
+
+       adb_ro_obj(pkg, ADBI_PKG_PATHS, &paths);
+
+       for (int p = ADBI_FIRST; p <= adb_ra_num(&paths); p++) {
+               adb_ro_obj(&paths, p, &path);
+               apk_blob_t pathname = adb_ro_blob(&path, ADBI_DI_NAME);
+               if (pathname.len && apk_fs_is_malicious_pathname(pathname)) 
return -APKE_FORMAT_INVALID;
+               adb_ro_obj(&path, ADBI_DI_FILES, &files);
+
+               for (int f = ADBI_FIRST; f <= adb_ra_num(&files); f++) {
+                       adb_ro_obj(&files, f, &file);
+                       if (apk_fs_is_malicious_filename(adb_ro_blob(&file, 
ADBI_FI_NAME))) return -APKE_FORMAT_INVALID;
+               }
+       }
+       return 0;
+}
+
 static int apk_extract_v3_next_file(struct apk_extract_ctx *ectx)
 {
        struct apk_extract_v3_ctx *ctx = ectx->pctx;
@@ -149,6 +170,9 @@
                ctx->cur_file = ADBI_FIRST;
                adb_r_rootobj(&ctx->db, &ctx->pkg, &schema_package);
 
+               r = apk_extract_v3_validate_filetree(&ctx->pkg);
+               if (r < 0) return r;
+
                r = ectx->ops->v3meta(ectx, &ctx->pkg);
                if (r < 0) return r;
 
@@ -175,9 +199,7 @@
 
                for (; ctx->cur_file <= adb_ra_num(&ctx->files); 
ctx->cur_file++) {
                        adb_ro_obj(&ctx->files, ctx->cur_file, &ctx->file);
-
                        n = apk_pathbuilder_pushb(&ctx->pb, 
adb_ro_blob(&ctx->file, ADBI_FI_NAME));
-
                        target = adb_ro_blob(&ctx->file, ADBI_FI_TARGET);
                        if (adb_ro_int(&ctx->file, ADBI_FI_SIZE) != 0 && 
APK_BLOB_IS_NULL(target))
                                return 0;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apk-tools-3.0.6/src/fs_fsys.c 
new/apk-tools-3.0.7/src/fs_fsys.c
--- old/apk-tools-3.0.6/src/fs_fsys.c   2026-04-13 10:36:44.000000000 +0200
+++ new/apk-tools-3.0.7/src/fs_fsys.c   2026-07-28 13:28:06.000000000 +0200
@@ -266,6 +266,28 @@
        return &fsdir_ops_fsys;
 }
 
+bool apk_fs_is_malicious_filename(apk_blob_t file)
+{
+       const uint8_t *ptr = (const uint8_t *) file.ptr;
+       for (int i = 0; i < file.len; i++) {
+               if (ptr[i] < 0x20 || ptr[i] == '/' || ptr[i] == 0x7f) return 
true;
+       }
+       switch (file.len) {
+       case 2: if (ptr[1] != '.') break; // fallthrough
+       case 1: if (ptr[0] != '.') break; // fallthrough
+       case 0: return true;
+       }
+       return false;
+}
+
+bool apk_fs_is_malicious_pathname(apk_blob_t path)
+{
+       apk_blob_t dir;
+       while (apk_blob_split(path, APK_BLOB_STRLIT("/"), &dir, &path))
+               if (apk_fs_is_malicious_filename(dir)) return true;
+       return path.len > 0 && apk_fs_is_malicious_filename(path);
+}
+
 static bool need_checksum(const struct apk_file_info *fi)
 {
        switch (fi->mode & S_IFMT) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apk-tools-3.0.6/src/genhelp.lua 
new/apk-tools-3.0.7/src/genhelp.lua
--- old/apk-tools-3.0.6/src/genhelp.lua 2026-04-13 10:36:44.000000000 +0200
+++ new/apk-tools-3.0.7/src/genhelp.lua 2026-07-28 13:28:06.000000000 +0200
@@ -21,6 +21,33 @@
  - From OPTIONS take each option and it's first sentence (within the first 
line)
 --]]
 
+-- Take the first sentence of a (possibly multi-line) description.
+-- stop at ". " or a trailing ".".
+function first_sentence(text)
+       local i = text:find("%.%s")
+       if not i then i = text:find("%.$") end
+       if i then text = text:sub(1, i - 1) end
+       return text
+end
+
+-- Name completion rule per applet
+applet_argument_completion = {
+       add="available",
+       del="installed",
+       dot="available",
+       fetch="available",
+       fix="installed",
+       index="available",
+       info="available",
+       list="available",
+       manifest="available",
+       mkndx="available",
+       policy="available",
+       query="available",
+       search="available",
+       upgrade="installed",
+}
+
 local scdoc = {
        usage_prefix = "Usage: ",
 }
@@ -168,7 +195,7 @@
                f:write(data)
                f:close()
 
-               local p = io.popen(('gzip -%d < %s'):format(level, tmp), 'r')
+               local p = io.popen(('gzip -n%d < %s'):format(level, tmp), 'r')
                if p ~= nil then
                        ret = p:read("*all")
                        p:close()
@@ -187,8 +214,8 @@
        for _, fn in ipairs(arg) do
                if fn == '--debug' then
                        self.debug = true
-               elseif fn == '--format=bash' then
-                       self.format = "bash"
+               elseif fn:match("^%-%-format=") then
+                       self.format = fn:match("^%-%-format=(.*)$")
                else
                        doc = new_scdoc()
                        doc:parse(fn)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apk-tools-3.0.6/src/genhelp_apk.lua 
new/apk-tools-3.0.7/src/genhelp_apk.lua
--- old/apk-tools-3.0.6/src/genhelp_apk.lua     2026-04-13 10:36:44.000000000 
+0200
+++ new/apk-tools-3.0.7/src/genhelp_apk.lua     2026-07-28 13:28:06.000000000 
+0200
@@ -48,10 +48,7 @@
                        table.insert(out, fmt:format(k, v))
                end
 
-               v = table.concat(v, " ")
-               local i = v:find("%.%s")
-               if not i then i = v:find("%.$") end
-               if i then v = v:sub(1, i-1) end
+               v = first_sentence(table.concat(v, " "))
                v = textwrap(v, width - nindent - 1)
 
                table.insert(out, v[1])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apk-tools-3.0.6/src/genhelp_bash.lua 
new/apk-tools-3.0.7/src/genhelp_bash.lua
--- old/apk-tools-3.0.6/src/genhelp_bash.lua    2026-04-13 10:36:44.000000000 
+0200
+++ new/apk-tools-3.0.7/src/genhelp_bash.lua    2026-07-28 13:28:06.000000000 
+0200
@@ -73,25 +73,9 @@
        return opts
 end
 
-local wildcard_installed = 'COMPREPLY=($(compgen -W "$(__apk_installed_pkgs 
"${cur}")" -- ${cur}))'
-local wildcard_available = 'COMPREPLY=($(compgen -W "$(__apk_available_pkgs 
"${cur}")" -- ${cur}))'
-
--- Applets needing package name completion
-local applet_wildcard = {
-       add=wildcard_available,
-       del=wildcard_installed,
-       dot=wildcard_available,
-       fetch=wildcard_available,
-       fix=wildcard_installed,
-       index=wildcard_available,
-       info=wildcard_available,
-       list=wildcard_available,
-       manifest=wildcard_available,
-       mkndx=wildcard_available,
-       policy=wildcard_available,
-       query=wildcard_available,
-       search=wildcard_available,
-       upgrade=wildcard_installed,
+local completion_function = {
+       available='COMPREPLY=($(compgen -W "$(__apk_available_pkgs "${cur}")" 
-- ${cur}))',
+       installed='COMPREPLY=($(compgen -W "$(__apk_installed_pkgs "${cur}")" 
-- ${cur}))',
 }
 
 function M:generate(app, docs)
@@ -113,7 +97,7 @@
        local t_applet = {}
        for _, name in ipairs(applets) do
                local o = table.concat(options[name], " ")
-               local wildcard = applet_wildcard[name] or "COMPREPLY=()"
+               local wildcard = 
completion_function[applet_argument_completion[name]] or "COMPREPLY=()"
                table.insert(t_applet, template_applet:format(name, o, 
wildcard))
        end
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apk-tools-3.0.6/src/genhelp_zsh.lua 
new/apk-tools-3.0.7/src/genhelp_zsh.lua
--- old/apk-tools-3.0.6/src/genhelp_zsh.lua     1970-01-01 01:00:00.000000000 
+0100
+++ new/apk-tools-3.0.7/src/genhelp_zsh.lua     2026-07-28 13:28:06.000000000 
+0200
@@ -0,0 +1,175 @@
+#!/usr/bin/lua5.3
+-- Generate Zsh completion for apk(8) from scdoc manpages
+
+local template_main = [==[
+#compdef apk
+
+# apk(8) completion                                         *- shell-script -*-
+# Automatically generated by apk-tools (src/genhelp_zsh.lua). Do not modify.
+
+__apk_installed_pkgs() {
+       local -a pkgs
+       pkgs=(${(f)"$(apk query --summarize name --installed "${PREFIX}*" 
2>/dev/null)"})
+       compadd -a pkgs
+}
+
+__apk_available_pkgs() {
+       local -a pkgs
+       pkgs=(${(f)"$(apk query --summarize name --available "${PREFIX}*" 
2>/dev/null)"})
+       compadd -a pkgs
+}
+
+_apk() {
+       local curcontext="$curcontext" state line
+       typeset -A opt_args
+
+       _arguments -C \
+%s             '1: :->applet' \
+               '*::arg:->args'
+
+       case $state in
+               applet)
+                       local -a applets
+                       applets=(
+%s                     )
+                       _describe -t applets 'apk command' applets
+                       ;;
+               args)
+                       case $line[1] in
+%s                     esac
+                       ;;
+       esac
+}
+
+_apk "$@"
+]==]
+
+local M = {}
+
+-- Escape text for use inside a single-quoted zsh _arguments spec, where
+-- '[', ']' and ':' are field delimiters and must be escaped to appear
+-- literally, and embedded single quotes must close/re-open the string.
+local function zsh_escape(s)
+       s = s:gsub("\\", "\\\\")
+       s = s:gsub("%[", "\\[")
+       s = s:gsub("%]", "\\]")
+       s = s:gsub(":", "\\:")
+       s = s:gsub("'", "'\\''")
+       return s
+end
+
+-- Escape text for a 'name:description' entry consumed by _describe.
+local function describe_escape(s)
+       s = s:gsub("\\", "\\\\")
+       s = s:gsub(":", "\\:")
+       s = s:gsub("'", "'\\''")
+       return s
+end
+
+-- Applets name completion functions
+local completion_function = {
+       available="__apk_available_pkgs",
+       installed="__apk_installed_pkgs",
+}
+
+-- Build one or more zsh _arguments spec strings (each already wrapped in
+-- single quotes) for a single option entry { raw_flags, {desc_lines} }.
+local function build_opt_specs(opt)
+       local desc = zsh_escape(first_sentence(table.concat(opt[2], " ")))
+       local plain, specs = {}, {}
+
+       for flag in opt[1]:gmatch("(%-%-?[^ ,]+)") do
+               local base, val = flag:match("(.*)%[=(%w+)%]")
+               if val == "BOOL" then
+                       table.insert(specs, ("'%s[%s]'"):format(base, desc))
+                       local no_opt = base:gsub("^%-%-", "--no-")
+                       table.insert(specs, ("'%s[%s]'"):format(no_opt, desc))
+               elseif val then
+                       -- optional value, e.g. --sync[=AUTO]
+                       table.insert(specs, ("'%s=-[%s]:%s:'"):format(base, 
desc, val:lower()))
+               else
+                       table.insert(plain, flag)
+               end
+       end
+
+       -- Group plain boolean aliases (-h, --help) into one mutually-exclusive 
spec
+       if #plain == 1 then
+               table.insert(specs, ("'%s[%s]'"):format(plain[1], desc))
+       elseif #plain > 1 then
+               table.insert(specs, ("'(%s)'{%s}'[%s]'"):format(
+                       table.concat(plain, " "), table.concat(plain, ","), 
desc))
+       end
+
+       return specs
+end
+
+local function collect_arg_specs(doc)
+       local specs = {}
+       for _, group in ipairs(doc.optgroup or {}) do
+               for _, opt in ipairs(group) do
+                       for _, s in ipairs(build_opt_specs(opt)) do
+                               table.insert(specs, s)
+                       end
+               end
+       end
+       return specs
+end
+
+function M:generate(app, docs)
+       local applets, options, goptions = {}, {}, {}
+       local descriptions, main_doc = {}, nil
+
+       for _, doc in ipairs(docs) do
+               -- only process section 8
+               if doc.mansection == "8" then
+                       if doc.applet == "APK" then
+                               goptions = collect_arg_specs(doc)
+                               main_doc = doc
+                       else
+                               table.insert(applets, doc.applet)
+                               options[doc.applet] = collect_arg_specs(doc)
+                       end
+               end
+       end
+       table.sort(applets)
+
+       if main_doc then
+               for _, cmd in ipairs(main_doc.commands) do
+                       if type(cmd) == "table" then
+                               descriptions[cmd[1]] = cmd[2]
+                       end
+               end
+       end
+
+       local global_block = {}
+       for _, s in ipairs(goptions) do
+               table.insert(global_block, "\t\t" .. s .. " \\\n")
+       end
+
+       local applets_block = {}
+       for _, name in ipairs(applets) do
+               local d = describe_escape(descriptions[name] or "")
+               table.insert(applets_block, ("\t\t\t\t'%s:%s'\n"):format(name, 
d))
+       end
+
+       local cases_block = {}
+       for _, name in ipairs(applets) do
+               local opt_lines = {}
+               for _, s in ipairs(options[name]) do
+                       table.insert(opt_lines, "\t\t\t\t\t\t" .. s .. " \\\n")
+               end
+               local wildcard = 
completion_function[applet_argument_completion[name]]
+               local tail = wildcard
+                       and ("\t\t\t\t\t\t'*:package:%s'\n"):format(wildcard)
+                       or "\t\t\t\t\t\t'*:arg:_default'\n"
+               table.insert(cases_block, ("\t\t\t\t%s)\n\t\t\t\t\t_arguments 
\\\n%s%s\t\t\t\t\t;;\n"):format(
+                       name, table.concat(opt_lines), tail))
+       end
+
+       return string.format(template_main,
+               table.concat(global_block),
+               table.concat(applets_block),
+               table.concat(cases_block))
+end
+
+return M
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apk-tools-3.0.6/src/meson.build 
new/apk-tools-3.0.7/src/meson.build
--- old/apk-tools-3.0.6/src/meson.build 2026-04-13 10:36:44.000000000 +0200
+++ new/apk-tools-3.0.7/src/meson.build 2026-07-28 13:28:06.000000000 +0200
@@ -110,6 +110,18 @@
                install: true,
                install_dir: join_paths(get_option('datadir'), 
'bash-completion', 'completions')
        )
+
+       generated_zsh_completion = custom_target(
+               'apk.zsh-completion',
+               capture: true,
+               output: '_apk_zsh',
+               input: man_files,
+               command: [lua_bin, genhelp_script, '--format=zsh', '@INPUT@'],
+               depend_files: 'genhelp_zsh.lua',
+               env: lua_env,
+               install: true,
+               install_dir: join_paths(get_option('datadir'), 'zsh', 
'site-functions')
+       )
 else
        generated_help = custom_target(
                'help.h',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apk-tools-3.0.6/src/repoparser.c 
new/apk-tools-3.0.7/src/repoparser.c
--- old/apk-tools-3.0.6/src/repoparser.c        2026-04-13 10:36:44.000000000 
+0200
+++ new/apk-tools-3.0.7/src/repoparser.c        2026-07-28 13:28:06.000000000 
+0200
@@ -143,6 +143,7 @@
 static bool is_keyword(apk_blob_t word)
 {
        if (word.ptr[0] == '@') return false; // tag
+       if (word.ptr[0] == '$') return false; // variable
        return !is_url(word);
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apk-tools-3.0.6/src/tar.c 
new/apk-tools-3.0.7/src/tar.c
--- old/apk-tools-3.0.6/src/tar.c       2026-04-13 10:36:44.000000000 +0200
+++ new/apk-tools-3.0.7/src/tar.c       2026-07-28 13:28:06.000000000 +0200
@@ -12,6 +12,7 @@
 
 #include "apk_defines.h"
 #include "apk_tar.h"
+#include "apk_fs.h"
 
 struct tar_header {
        /* ustar header, Posix 1003.1 */
@@ -221,13 +222,18 @@
                        break;
                }
 
-               if (strnlen(entry.name, PATH_MAX) >= PATH_MAX-10 ||
+               size_t entry_len = strnlen(entry.name, PATH_MAX);
+               if (entry_len >= PATH_MAX-10 ||
                    (entry.link_target && strnlen(entry.link_target, PATH_MAX) 
>= PATH_MAX-10)) {
                        r = -ENAMETOOLONG;
                        goto err;
                }
 
                if (entry.mode & S_IFMT) {
+                       if 
(apk_fs_is_malicious_pathname(APK_BLOB_PTR_LEN((char*) entry.name, entry_len))) 
{
+                               r = -APKE_FORMAT_INVALID;
+                               goto err;
+                       }
                        apk_istream_segment(&segment, is, entry.size, 
entry.mtime);
                        r = parser(ctx, &entry, &segment.is);
                        if (r != 0) goto err;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apk-tools-3.0.6/test/testlib.sh 
new/apk-tools-3.0.7/test/testlib.sh
--- old/apk-tools-3.0.6/test/testlib.sh 2026-04-13 10:36:44.000000000 +0200
+++ new/apk-tools-3.0.7/test/testlib.sh 2026-07-28 13:28:06.000000000 +0200
@@ -9,6 +9,16 @@
        exit 1
 }
 
+skip_test() {
+       exit 77
+}
+
+require_programs() {
+       for PROG in "$@"; do
+               which "$PROG" > /dev/null || skip_test
+       done
+}
+
 glob_one() {
        # shellcheck disable=SC2048 # argument is wildcard needing expansion
        for a in $*; do
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apk-tools-3.0.6/test/unit/fs_test.c 
new/apk-tools-3.0.7/test/unit/fs_test.c
--- old/apk-tools-3.0.6/test/unit/fs_test.c     1970-01-01 01:00:00.000000000 
+0100
+++ new/apk-tools-3.0.7/test/unit/fs_test.c     2026-07-28 13:28:06.000000000 
+0200
@@ -0,0 +1,29 @@
+#include "apk_test.h"
+#include "apk_fs.h"
+
+APK_TEST(fs_malicious_filename) {
+       assert_false(apk_fs_is_malicious_filename(APK_BLOB_STRLIT("...")));
+       assert_false(apk_fs_is_malicious_filename(APK_BLOB_STRLIT("..bar")));
+       assert_false(apk_fs_is_malicious_filename(APK_BLOB_STRLIT(".bar")));
+       assert_false(apk_fs_is_malicious_filename(APK_BLOB_STRLIT("bar")));
+       assert_false(apk_fs_is_malicious_filename(APK_BLOB_STRLIT("båäö")));
+       assert_true(apk_fs_is_malicious_filename(APK_BLOB_STRLIT("")));
+       assert_true(apk_fs_is_malicious_filename(APK_BLOB_STRLIT(".")));
+       assert_true(apk_fs_is_malicious_filename(APK_BLOB_STRLIT("..")));
+       assert_true(apk_fs_is_malicious_filename(APK_BLOB_STRLIT("foobar/")));
+       assert_true(apk_fs_is_malicious_filename(APK_BLOB_STRLIT("foo/bar")));
+       
assert_true(apk_fs_is_malicious_filename(APK_BLOB_STRLIT("foo""\x00""bar")));
+       
assert_true(apk_fs_is_malicious_filename(APK_BLOB_STRLIT("foo""\x10""bar")));
+}
+
+APK_TEST(fs_malicious_pathname) {
+       assert_false(apk_fs_is_malicious_pathname(APK_BLOB_STRLIT("foo/foo")));
+       
assert_false(apk_fs_is_malicious_pathname(APK_BLOB_STRLIT("foo/..foo/foo")));
+       assert_false(apk_fs_is_malicious_pathname(APK_BLOB_STRLIT("foo/foo/")));
+       assert_false(apk_fs_is_malicious_pathname(APK_BLOB_STRLIT(".foo/foo")));
+       assert_true(apk_fs_is_malicious_pathname(APK_BLOB_STRLIT("../foo")));
+       assert_true(apk_fs_is_malicious_pathname(APK_BLOB_STRLIT("foo//foo")));
+       
assert_true(apk_fs_is_malicious_pathname(APK_BLOB_STRLIT("foo/../foo")));
+       assert_true(apk_fs_is_malicious_pathname(APK_BLOB_STRLIT("foo/./foo")));
+       
assert_true(apk_fs_is_malicious_pathname(APK_BLOB_STRLIT("foo/\x1f/foo")));
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apk-tools-3.0.6/test/unit/meson.build 
new/apk-tools-3.0.7/test/unit/meson.build
--- old/apk-tools-3.0.6/test/unit/meson.build   2026-04-13 10:36:44.000000000 
+0200
+++ new/apk-tools-3.0.7/test/unit/meson.build   2026-07-28 13:28:06.000000000 
+0200
@@ -4,6 +4,7 @@
 
 unit_test_src = [
        'blob_test.c',
+       'fs_test.c',
        'io_test.c',
        'package_test.c',
        'process_test.c',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apk-tools-3.0.6/test/unit/repoparser_test.c 
new/apk-tools-3.0.7/test/unit/repoparser_test.c
--- old/apk-tools-3.0.6/test/unit/repoparser_test.c     2026-04-13 
10:36:44.000000000 +0200
+++ new/apk-tools-3.0.7/test/unit/repoparser_test.c     2026-07-28 
13:28:06.000000000 +0200
@@ -70,18 +70,21 @@
                "set var-foo=bad-name\n"
                "set APK_FOO=reserved\n"
                "http://${mirror}/ ${comp}\n"
+               "set mirror=/alpine\n"
+               "${mirror}/main\n"
                "v2 foobar main\n",
                "WARNING: repositories:1: invalid option: -unknown\n"
                "WARNING: repositories:8: undefined variable: undefined\n"
                "WARNING: repositories:11: invalid variable definition: 
var-foo=bad-name\n"
                "WARNING: repositories:12: invalid variable definition: 
APK_FOO=reserved\n"
-               "WARNING: repositories:14: invalid url: foobar\n",
+               "WARNING: repositories:16: invalid url: foobar\n",
                "http://alpine.org/main:APKINDEX.tar.gz:\n";
                "http://example.com/main:APKINDEX.tar.gz:\n";
                "http://example.com/main:APKINDEX.tar.gz:\n";
                "http://example.com/alpine/main:APKINDEX.tar.gz:\n";
                "http://example.com/alpine/community:APKINDEX.tar.gz:\n";
                "http://example.com/alpine/testing:APKINDEX.tar.gz:\n";
+               "/alpine/main:APKINDEX.tar.gz:\n"
                );
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apk-tools-3.0.6/test/user/malicious.sh 
new/apk-tools-3.0.7/test/user/malicious.sh
--- old/apk-tools-3.0.6/test/user/malicious.sh  1970-01-01 01:00:00.000000000 
+0100
+++ new/apk-tools-3.0.7/test/user/malicious.sh  2026-07-28 13:28:06.000000000 
+0200
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+TESTDIR=$(realpath "${TESTDIR:-"$(dirname "$0")"/..}")
+. "$TESTDIR"/testlib.sh
+
+require_programs xxd
+
+setup_apkroot
+APK="$APK --allow-untrusted --no-interactive"
+
+mkdir -p files/a files/dir===dir
+echo hello > files/a/file=file
+
+$APK mkpkg --compression=none -I name:malicious -I version:1.0 -F files -o 
malicious-1.0.apk
+
+echo hello > files/a/"file     file"
+$APK mkpkg --compression=none -I name:malicious -I version:1.0 -F files -o 
malicious-1.0b.apk && assert "mkpkg filename check failed"
+
+mkdir dest dest-a dest-b
+$APK extract malicious-1.0.apk --destination dest || assert "failed to extract"
+
+xxd -p -c100000 malicious-1.0.apk | sed -e 
's,6469723d3d3d646972,6469722f2e2f646972,' | xxd -rp > malicious-1.0-a.apk
+$APK extract malicious-1.0-a.apk --destination dest-a && assert "malicious 
check 1 failed"
+
+xxd -p -c100000 malicious-1.0.apk | sed -e 
's,66696c653d66696c65,66696c652f66696c65,' | xxd -rp > malicious-1.0-b.apk
+$APK extract malicious-1.0-b.apk --destination dest-b && assert "malicious 
check 2 failed"
+
+exit 0

Reply via email to