Skip to content
This repository was archived by the owner on Jul 21, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
204 changes: 204 additions & 0 deletions Formula/unzip.rb
Original file line number Diff line number Diff line change
@@ -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
113 changes: 113 additions & 0 deletions Formula/zip.rb
Original file line number Diff line number Diff line change
@@ -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
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down