diff --git a/Formula/unzip.rb b/Formula/unzip.rb new file mode 100644 index 0000000..cb150f9 --- /dev/null +++ b/Formula/unzip.rb @@ -0,0 +1,204 @@ +require (Tap.fetch("automattic", "kandelo-homebrew").path/"Kandelo/formula_support/kandelo_formula_support").to_s + +class Unzip < Formula + include KandeloFormulaSupport + + desc "Extraction utility for ZIP archives on Kandelo" + homepage "https://infozip.sourceforge.net/UnZip.html" + url "https://downloads.sourceforge.net/project/infozip/UnZip%206.x%20%28latest%29/UnZip%206.0/unzip60.tar.gz" + version "6.0" + sha256 "036d96991646d0449ed0aa952e4fbe21b476ce994abc276e49d30e686708bd37" + license "Info-ZIP" + + depends_on "binaryen" => :build + depends_on "wabt" => :build + + skip_clean "bin/unzip", "bin/funzip", "bin/unzipsfx", "bin/zipinfo" + + # Upstream is unmaintained. Follow Homebrew's maintained formula and apply + # Ubuntu's complete security, correctness, and reproducibility quilt series. + patch do + url "https://archive.ubuntu.com/ubuntu/pool/main/u/unzip/unzip_6.0-28ubuntu4.1.debian.tar.xz" + sha256 "d123c8e6972dbdd17ba1a4920fb57ed2ede9237dbae149dcbf55df829c77baf3" + apply %w[ + patches/01-manpages-in-section-1-not-in-section-1l.patch + patches/02-this-is-debian-unzip.patch + patches/03-include-unistd-for-kfreebsd.patch + patches/04-handle-pkware-verification-bit.patch + patches/05-fix-uid-gid-handling.patch + patches/06-initialize-the-symlink-flag.patch + patches/07-increase-size-of-cfactorstr.patch + patches/08-allow-greater-hostver-values.patch + patches/09-cve-2014-8139-crc-overflow.patch + patches/10-cve-2014-8140-test-compr-eb.patch + patches/11-cve-2014-8141-getzip64data.patch + patches/12-cve-2014-9636-test-compr-eb.patch + patches/13-remove-build-date.patch + patches/14-cve-2015-7696.patch + patches/15-cve-2015-7697.patch + patches/16-fix-integer-underflow-csiz-decrypted.patch + patches/17-restore-unix-timestamps-accurately.patch + patches/18-cve-2014-9913-unzip-buffer-overflow.patch + patches/19-cve-2016-9844-zipinfo-buffer-overflow.patch + patches/20-unzip60-alt-iconv-utf8.patch + patches/20-cve-2018-1000035-unzip-buffer-overflow.patch + patches/21-fix-warning-messages-on-big-files.patch + patches/22-cve-2019-13232-fix-bug-in-undefer-input.patch + patches/23-cve-2019-13232-zip-bomb-with-overlapped-entries.patch + patches/24-cve-2019-13232-do-not-raise-alert-for-misplaced-central-directory.patch + patches/25-cve-2019-13232-fix-bug-in-uzbunzip2.patch + patches/26-cve-2019-13232-fix-bug-in-uzinflate.patch + patches/27-zipgrep-avoid-test-errors.patch + patches/28-cve-2022-0529-and-cve-2022-0530.patch + patches/handle_windows_zip64.patch + patches/29-fix-troff-warning.patch + patches/CVE-2021-4217.patch + ] + end + + def install + kandelo_require_arch!("wasm32") + + kandelo_wasm_build do + cflags = %w[ + -O2 + -Wall + -I. + -DUNIX + -DSYSV + -DMODERN + -Dlinux + -DHAVE_UNISTD_H + -DHAVE_DIRENT_H + -DHAVE_TERMIOS_H + -DACORN_FTYPE_NFS + -DWILD_STOP_AT_DIR + -DLARGE_FILE_SUPPORT + -DUNICODE_SUPPORT + -DUNICODE_WCHAR + -DUTF8_MAYBE_NATIVE + -DNO_WORKING_ISPRINT + -DNO_LCHMOD + -DDATE_FORMAT=DF_YMD + -DIZ_HAVE_STRDUP + -DIZ_HAVE_STRCASECMP + ] + system "make", "-f", "unix/Makefile", + "CC=#{kandelo_cc}", + "CF=#{cflags.join(" ")}", + "LF2=", + "unzips" + %w[unzip funzip unzipsfx].each do |program| + kandelo_validate_wasm_artifact(buildpath/program, fork: :forbidden) + end + system "make", "-f", "unix/Makefile", + "BINDIR=#{bin}", + "MANDIR=#{man1}", + "install" + end + + # zipgrep relies on Kandelo's POSIX base shell, egrep, sed, and basename; + # same-keg unzip is its only non-base command. + File.open(man1/"unzipsfx.1", "a") do |manual| + manual.write <<~MANPAGE + .SH KANDELO WASM PACKAGING + A Kandelo self-extractor must remain a valid WebAssembly module, so a ZIP archive + cannot be concatenated directly to unzipsfx. Use the Kandelo SDK toolchain to + embed the archive in a custom section: + .PP + .nf + llvm-objcopy --add-section kandelo.sfx=archive.zip unzipsfx.wasm output.wasm + .fi + MANPAGE + end + end + + test do + archive = testpath/"fixture.zip" + archive.binwrite( + "UEsDBBQAAAAIAAAAIVxY+qxoIAAAAMAEAAAJAAAAYWxwaGEudHh0S8wpyEhUSCvKz1XwTsxLSc3J50ocFRoV" \ + "GhUaFRoKQgBQSwMEFAAAAAgAAAAhXJsvleEdAAAAYAMAAA8AAABuZXN0ZWQvYmV0YS50eHRLSi1JVEgrys9V" \ + "8E7MS0nNyedKGhUZFRkVoZIIAFBLAQIeAxQAAAAIAAAAIVxY+qxoIAAAAMAEAAAJAAAAAAAAAAEAAACkgQAA" \ + "AABhbHBoYS50eHRQSwECHgMUAAAACAAAACFcmy+V4R0AAABgAwAADwAAAAAAAAABAAAApIFHAAAAbmVzdGVk" \ + "L2JldGEudHh0UEsFBgAAAAACAAIAdAAAAJEAAAAAAA==".unpack1("m0"), + ) + cwd_env = { "KERNEL_CWD" => testpath } + + listing = kandelo_run_wasm(bin/"unzip", ["-l", "fixture.zip"], env: cwd_env) + assert_match(/alpha\.txt/, listing) + assert_match(%r{nested/beta\.txt}, listing) + assert_match(/2 files/, listing) + + extracted = testpath/"extracted" + extracted.mkpath + assert_empty kandelo_run_wasm( + bin/"unzip", ["-q", "fixture.zip", "-d", "extracted"], env: cwd_env + ) + assert_equal "alpha from Kandelo\n" * 64, (extracted/"alpha.txt").read + assert_equal "beta from Kandelo\n" * 48, (extracted/"nested/beta.txt").read + + assert_equal "alpha.txt\nnested/beta.txt\n", + kandelo_run_wasm(bin/"unzip", ["-Z", "-1", "fixture.zip"], env: cwd_env) + assert_match(/^ZipInfo 3\.00/, kandelo_run_wasm(bin/"zipinfo", ["-h"], preserve_argv0: true)) + assert_match(/^UnZipSFX 6\.00/, kandelo_run_wasm(bin/"unzipsfx", ["-h"])) + + zipgrep = (bin/"zipgrep").read + assert_equal "#!/bin/sh\n", zipgrep.each_line.first + %w[egrep sed basename unzip].each { |command| assert_match(/\b#{command}\b/, zipgrep) } + %w[funzip unzip unzipsfx zipgrep zipinfo].each do |program| + assert_path_exists man1/"#{program}.1" + end + assert_includes (man1/"unzipsfx.1").read, "llvm-objcopy --add-section kandelo.sfx=" + + encode_uleb = lambda do |value| + encoded = +"".b + loop do + byte = value & 0x7f + value >>= 7 + byte |= 0x80 unless value.zero? + encoded << byte + break if value.zero? + end + encoded + end + section_name = "kandelo.sfx".b + section_payload = encode_uleb.call(section_name.bytesize) + section_name + archive.binread + self_extractor = testpath/"fixture-sfx.wasm" + self_extractor.binwrite( + (bin/"unzipsfx").binread + "\0".b + encode_uleb.call(section_payload.bytesize) + section_payload, + ) + self_extractor.chmod 0755 + guest_sfx = "/usr/local/bin/fixture-sfx.wasm" + sfx_program = { guest_sfx => self_extractor } + sfx_listing = kandelo_run_wasm( + self_extractor, ["-t"], argv0: guest_sfx, exec_programs: sfx_program + ) + assert_match(/testing: alpha\.txt/, sfx_listing) + assert_match(%r{testing: nested/beta\.txt}, sfx_listing) + + sfx_extracted = testpath/"sfx-extracted" + sfx_extracted.mkpath + sfx_output = kandelo_run_wasm( + self_extractor, + ["-q", "-d", "/work"], + argv0: guest_sfx, + exec_programs: sfx_program, + writable_host_directories: { "/work" => sfx_extracted }, + ) + assert_match(/^UnZipSFX 6\.00 /, sfx_output) + assert_equal "alpha from Kandelo\n" * 64, (sfx_extracted/"alpha.txt").read + assert_equal "beta from Kandelo\n" * 48, (sfx_extracted/"nested/beta.txt").read + + funzip_archive = + "UEsDBBQAAAAIAAAAIVxY+qxoIAAAAMAEAAAJAAAAYWxwaGEudHh0S8wpyEhUSCvKz1XwTsxLSc3J50ocFRoV" \ + "GhUaFRoKQgBQSwECHgMUAAAACAAAACFcWPqsaCAAAADABAAACQAAAAAAAAABAAAApIEAAAAAYWxwaGEudHh0" \ + "UEsFBgAAAAABAAEANwAAAEcAAAAAAA==".unpack1("m0") + assert_equal "alpha from Kandelo\n" * 64, + kandelo_run_wasm(bin/"funzip", [], stdin: funzip_archive, preserve_argv0: true) + + missing = kandelo_run_wasm( + bin/"unzip", ["missing.zip"], env: cwd_env, merge_stderr: true, expected_status: 9 + ) + assert_match(/cannot find or open missing\.zip/, missing) + end +end diff --git a/Formula/zip.rb b/Formula/zip.rb new file mode 100644 index 0000000..bb0c538 --- /dev/null +++ b/Formula/zip.rb @@ -0,0 +1,113 @@ +require (Tap.fetch("automattic", "kandelo-homebrew").path/"Kandelo/formula_support/kandelo_formula_support").to_s + +class Zip < Formula + include KandeloFormulaSupport + + desc "Compression and archive utility for Kandelo" + homepage "https://infozip.sourceforge.net/Zip.html" + url "https://downloads.sourceforge.net/project/infozip/Zip%203.x%20%28latest%29/3.0/zip30.tar.gz" + version "3.0" + sha256 "f0e8bb1f9b7eb0b01285495a2699df3a4b766784c1765a8f1aeedf63c0806369" + license "Info-ZIP" + + depends_on "binaryen" => :build + depends_on "wabt" => :build + depends_on "automattic/kandelo-homebrew/unzip" + + skip_clean "bin/zip", "bin/zipcloak", "bin/zipnote", "bin/zipsplit" + + # Upstream is unmaintained. Follow Homebrew's maintained formula and apply + # Debian's security and reproducibility fixes at this upstream boundary. + patch do + url "https://deb.debian.org/debian/pool/main/z/zip/zip_3.0-15.debian.tar.xz" + sha256 "6dc1711c67640e8d1dee867ff53e84387ddb980c40885bd088ac98c330bffce9" + type :unofficial + apply %w[ + patches/01-typo-it-is-transferring-not-transfering.patch + patches/02-typo-it-is-privileges-not-priviliges.patch + patches/03-manpages-in-section-1-not-in-section-1l.patch + patches/04-do-not-set-unwanted-cflags.patch + patches/05-typo-it-is-preceding-not-preceeding.patch + patches/06-stack-markings-to-avoid-executable-stack.patch + patches/07-fclose-in-file-not-fclose-x.patch + patches/08-hardening-build-fix-1.patch + patches/09-hardening-build-fix-2.patch + patches/10-remove-build-date.patch + patches/11-typo-it-is-ambiguities-not-amgibuities.patch + patches/13-typo-it-is-os-2-not-risc-os-2.patch + patches/14-buffer-overflow-unicode-filename.patch + patches/15-buffer-overflow-cve-2018-13410.patch + patches/16-fix-symlink-update-detection.patch + ] + end + + def install + kandelo_require_arch!("wasm32") + + kandelo_wasm_build do + system "make", "-f", "unix/Makefile", + "CC=#{kandelo_cc}", + "CPP=#{kandelo_cc} -E", + "CFLAGS=-I. -DUNIX -O2 -DUIDGID_NOT_16BIT -DHAVE_DIRENT_H -DHAVE_TERMIOS_H " \ + "-DLARGE_FILE_SUPPORT", + "OBJA=", + "OCRCU8=crc32_.o ", + "OCRCTB=", + "LFLAGS1=", + "LFLAGS2=", + "LN=ln -s", + "IZ_BZIP2=", + "LIB_BZ=", + "zips" + %w[zip zipcloak zipnote zipsplit].each do |program| + kandelo_validate_wasm_artifact(buildpath/program, fork: :forbidden) + end + system "make", "-f", "unix/Makefile", + "BINDIR=#{bin}", + "MANDIR=#{man1}", + "install" + end + end + + test do + inputs = testpath/"inputs" + (inputs/"nested").mkpath + (inputs/"alpha.txt").write("alpha from Kandelo\n") + (inputs/"nested/beta.txt").write("beta from Kandelo\n") + unzip = inputs/"unzip" + unzip.binwrite((formula_opt_bin("automattic/kandelo-homebrew/unzip")/"unzip").binread) + unzip.chmod 0755 + cwd_env = { "KERNEL_CWD" => inputs, "KERNEL_PATH" => inputs } + + assert_empty kandelo_run_wasm( + bin/"zip", ["-q", "archive.zip", "alpha.txt", "nested/beta.txt"], env: cwd_env + ) + assert_path_exists inputs/"archive.zip" + + listing = kandelo_run_wasm(bin/"zip", ["-sf", "archive.zip"], env: cwd_env) + assert_match(/^ alpha\.txt$/, listing) + assert_match(%r{^ nested/beta\.txt$}, listing) + assert_match(/Total 2 entries/, listing) + + integrity = kandelo_run_wasm(bin/"zip", ["-T", "archive.zip"], env: cwd_env) + assert_match(/test of archive\.zip OK/, integrity) + + zipnote = kandelo_run_wasm(bin/"zipnote", ["archive.zip"], env: cwd_env) + assert_match(/^@ alpha\.txt$/, zipnote) + assert_match(%r{^@ nested/beta\.txt$}, zipnote) + assert_match(/ZipCloak 3\.0/, kandelo_run_wasm(bin/"zipcloak", ["-h"])) + assert_match(/ZipSplit 3\.0/, kandelo_run_wasm(bin/"zipsplit", ["-h"])) + assert_match(/1 zip files would be made/, + kandelo_run_wasm(bin/"zipsplit", ["-t", "archive.zip"], env: cwd_env)) + + %w[zip zipcloak zipnote zipsplit].each do |program| + assert_path_exists man1/"#{program}.1" + end + + nothing_to_do = kandelo_run_wasm( + bin/"zip", ["empty.zip"], env: cwd_env, merge_stderr: true, expected_status: 12 + ) + assert_match(/Nothing to do/, nothing_to_do) + refute_path_exists inputs/"empty.zip" + end +end diff --git a/README.md b/README.md index 03a785d..897715d 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,8 @@ Current migration controls and pilots include: - `m4`, the GNU macro processor with process-executing builtins backed by the tap's Dash shell; - `gawk`, GNU's pattern scanning and text-processing language; - `file`, compression-aware file type identification backed by the complete - `libmagic` database. + `libmagic` database; +- `zip` and `unzip`, the security-patched Info-ZIP creation, extraction, and inspection tools. The SDK is not yet a Homebrew dependency. Trusted builds supply an `HOMEBREW_KANDELO_ROOT` checkout containing the SDK, sysroot, kernel, and Node