chainguard-dev / go-apk

native go library for installation and management of apk packages
Apache License 2.0
29 stars 27 forks source link

go-apk allows for creating packages with invalid versions #174

Open joemiller opened 9 months ago

joemiller commented 9 months ago

I came across a slightly obscure bug, or at least I think it's a bug. I was building a package with melange that contained a version string with dashes and I noticed that alpine's apk would fail with package file format error on a specific case: A sub package with a provides = line containing an invalid version string. apk seemed happy to install packages with invalid versions in them but fail on packages with invalid versions in the .PKGINFO header file. In my case I found this happen on a provides = cmd:foo-$BAD_VERSION line, but it could happen on depends = lines too.

In our case the version string we were using was roughly: 8.0.0.20231201-ps-f1234567

Here is a reproducible example. I'll use the zlib.yaml from the melange/os repo. It doesn't matter the package, I just needed something quick to compile for the reproduction example.

I have made two changes to zlib.yaml from the original melange version:

  1. version modified to add -f12345678. Adding anything containing a dash is the key here.
  2. modified the dev subpackage to add a "binary" /usr/bin/foo, since the issue does not seem to appear until apk attempts to parse the provides = cmd:foo[VERSION] line of the dev package's .PKGINFO file.
package:
  name: zlib
  version: "1.3-f12345678"   # <---------------------- MODIFIED from original
  epoch: 0
  description: "a library implementing the zlib compression algorithms"
  copyright:
    - license: MPL-2.0 AND MIT

environment:
  contents:
    repositories:
      - https://packages.wolfi.dev/os
    keyring:
      - https://packages.wolfi.dev/os/wolfi-signing.rsa.pub
    packages:
      - autoconf
      - automake
      - build-base
      - busybox
      - ca-certificates-bundle
      - libtool
      - wolfi-baselayout

pipeline:
  - uses: fetch
    with:
      #uri: https://zlib.net/zlib-${{package.version}}.tar.gz
      uri: https://zlib.net/zlib-1.3.tar.gz
      expected-sha256: ff0ba4c292013dbc27530b3a81e1f9a813cd39de01ca5e0f8bf355702efa593e

  - runs: |
      CHOST="${{host.triplet.gnu}}" ./configure \
        --prefix=/usr \
        --libdir=/lib \
        --shared

  - uses: autoconf/make

  - runs: |
      make install pkgconfigdir="/usr/lib/pkgconfig" DESTDIR="${{targets.destdir}}"

  - uses: strip

subpackages:
  - name: "zlib-static"
    description: "zlib static library"
    pipeline:
      - uses: split/static

  - name: "zlib-dev"
    description: "zlib development headers"
    pipeline:
      - uses: split/dev
      # ------------ ADDED /usr/bin/foo to ensure a `provides = cmd:foo[VERSION` line is added to the dev package's .PKGINFO FILE: ------------
      - runs: |
          mkdir -p ${{targets.subpkgdir}}/usr/bin
          echo '#!/bin/sh' >${{targets.subpkgdir}}/usr/bin/test-foo
          echo 'echo hello' >>${{targets.subpkgdir}}/usr/bin/test-foo
          chmod +x ${{targets.subpkgdir}}/usr/bin/test-foo
    dependencies:
      runtime:
        - zlib

  - name: "minizip"
    description: "a library for manipulation with files from .zip archives"
    pipeline:
      - runs: |
          cd contrib/minizip
          autoreconf -fiv
          ./configure --prefix=/usr \
            --enable-static=no
          make
          make install DESTDIR="${{targets.contextdir}}"
melange keygen
melange build --arch host --source-dir . -k melange.rsa ./zlib.yaml
$ docker run --rm -it -v$PWD:/pkg cgr.dev/chainguard/sdk

❯ apk add -s -v --allow-untrusted /pkg/packages/x86_64/zlib-dev-1.3-f12345678-r0.apk

ERROR: /pkg/packages/x86_64/zlib-dev-1.3-f12345678-r0.apk: package file format error

Modify the zlib.yaml and set version: 1.3 and the dev package will work fine.

I am not sure if this bug should be in this repo or the melange repo, nor what the exact fix should be, other than to match the version validation logic from https://gitlab.alpinelinux.org/alpine/apk-tools/-/blob/2.14-stable/src/version.c

EDIT: After further testing it appears that only a single _ is allowed in the version string. The same version validation failure will occur on a version string like version: 1.3_f12345678_foo

joemiller commented 9 months ago

After doing some more testing on this it is not as simple as multiple _ in a version string.

The _ is recognized as the start of TOKEN_SUFFIX separator: https://gitlab.alpinelinux.org/alpine/apk-tools/-/blob/2.14-stable/src/version.c?ref_type=heads#L46-47

And the expectation for what is allowed next is here: https://gitlab.alpinelinux.org/alpine/apk-tools/-/blob/2.14-stable/src/version.c?ref_type=heads#L105-124

git is listed as one of the recognized post_suffixes: https://gitlab.alpinelinux.org/alpine/apk-tools/-/blob/2.14-stable/src/version.c?ref_type=heads#L74

But in my testing it appears that only strings with numerics following git are allowed:

(TIL: apk version sub command can be used to exercise the version validation and comparison logic):

$ apk version -c 8.0.34.20231212_git1123456789-r0 ; echo $?
0

$ apk version -c 8.0.34.20231212_gitb2345a6789-r0 ; echo $?
1

I don't see any test cases for the git post-suffix so it would not surprise me if this is a bug, as I don't see how the git suffix would be useful with strings only containing numerics. Test cases: https://gitlab.alpinelinux.org/alpine/apk-tools/-/blob/2.14-stable/test/version.data?ref_type=heads

joemiller commented 9 months ago

TL;DR - alpine uses Gentoo's versioning scheme (https://gitlab.alpinelinux.org/alpine/apk-tools/-/blob/2.14-stable/src/version.c?ref_type=heads#L15) which is a bit limited compared to opkg/debian/pkgconfig's algorithm, and there may be work to change this in the future, possibly a v3.x release of apk-tools: https://gitlab.alpinelinux.org/alpine/apk-tools/-/issues/10830