commit 56755e162db1b89015e3320757a37fcfb8ddbfeb
parent 2b750a220d91fbbd5b41fbed5be909b1445eb1c7
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Thu, 28 May 2026 18:32:29 -0700
pkg: canonicalize artifact order
Diffstat:
3 files changed, 472 insertions(+), 47 deletions(-)
diff --git a/doc/DISTRIBUTE.md b/doc/DISTRIBUTE.md
@@ -252,14 +252,20 @@ cfree pkg create --name N --version V [--desc D] -s SECKEY \
[--format cfpkg|tar.gz] [--compression none|lz4-block-v1] \
-o OUT FILE...
cfree pkg verify [-p PUBKEY | --tofu] [--format cfpkg|tar.gz] FILE
-cfree pkg unpack FILE -C DIR
+cfree pkg unpack [--verify] [-p PUBKEY | --tofu] [--format cfpkg|tar.gz] \
+ FILE -C DIR
cfree pkg inspect FILE
-cfree pkg trust {list | add PUBKEY [label] | remove KEYID}
+cfree pkg trust {path | list | add PUBKEY [label] | remove KEYID}
```
`create` infers the physical representation from `-o`: `.cfpkg` is native,
`.tar.gz` is portable. `--format` overrides inference.
+`pkg trust path` prints the trusted-keys file path after applying
+`$CFREE_TRUSTED_KEYS` / `$HOME` resolution. `pkg unpack` verifies before writing
+artifacts; `--verify` makes that verification explicit in the command and emits
+the normal verification success line.
+
## Implementation status
Implemented:
diff --git a/driver/pkg.c b/driver/pkg.c
@@ -2,6 +2,7 @@
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include "dist/blake2b.h"
@@ -145,12 +146,19 @@ static void pkg_region_root(uint8_t out[DIST_BLAKE2B_LEN], const char* kind,
dist_cfpkg2_root_hash(out, kind, 0, h);
}
+static int pkg_input_cmp(const void* ap, const void* bp) {
+ const PkgInputFile* a = (const PkgInputFile*)ap;
+ const PkgInputFile* b = (const PkgInputFile*)bp;
+ int c = strcmp(a->path, b->path);
+ if (c != 0) return c;
+ return strcmp(a->src, b->src);
+}
+
static int pkg_load_inputs(const CfreeContext* ctx, const char** files,
size_t n_files, PkgInputFile* in, DistManifest* m) {
size_t i;
memset(m, 0, sizeof *m);
for (i = 0; i < n_files; ++i) {
- DistArtifact* a;
in[i].src = files[i];
in[i].path = driver_basename(files[i]);
if (pkg_read_file(ctx, files[i], &in[i].fd) != DIST_OK) {
@@ -162,6 +170,16 @@ static int pkg_load_inputs(const CfreeContext* ctx, const char** files,
driver_errf(PKG_TOOL, "create: too many artifacts");
return DIST_ERR;
}
+ }
+
+ qsort(in, n_files, sizeof in[0], pkg_input_cmp);
+
+ for (i = 0; i < n_files; ++i) {
+ DistArtifact* a;
+ if (i > 0 && driver_streq(in[i - 1].path, in[i].path)) {
+ driver_errf(PKG_TOOL, "create: duplicate artifact path: %s", in[i].path);
+ return DIST_ERR;
+ }
a = &m->artifacts[m->n_artifacts++];
a->id = (uint64_t)i;
snprintf(a->path, sizeof a->path, "%s", in[i].path);
@@ -207,9 +225,9 @@ void driver_help_pkg(void) {
" [--format cfpkg|tar.gz] [--compression none|lz4-block-v1]\n"
" -o OUT FILE...\n"
" cfree pkg verify [-p PUBKEY | --tofu] [--format cfpkg|tar.gz] FILE\n"
- " cfree pkg unpack FILE -C DIR\n"
+ " cfree pkg unpack [--verify] [-p PUBKEY | --tofu] [--format cfpkg|tar.gz] FILE -C DIR\n"
" cfree pkg inspect FILE\n"
- " cfree pkg trust {list | add PUBKEY [label] | remove KEYID}\n");
+ " cfree pkg trust {path | list | add PUBKEY [label] | remove KEYID}\n");
}
static int pkg_keygen(DriverEnv* env, const CfreeContext* ctx, int argc,
@@ -989,13 +1007,15 @@ done:
static int pkg_verify_or_unpack(DriverEnv* env, const CfreeContext* ctx,
int argc, char** argv, int unpack) {
const char *file = NULL, *pubkey = NULL, *dir = ".";
- int tofu = 0, i;
+ int tofu = 0, explicit_verify = 0, i;
PkgFormat fmt = PKG_FMT_AUTO;
for (i = 0; i < argc; ++i) {
if (driver_streq(argv[i], "-p") && i + 1 < argc)
pubkey = argv[++i];
else if (driver_streq(argv[i], "--tofu"))
tofu = 1;
+ else if (unpack && driver_streq(argv[i], "--verify"))
+ explicit_verify = 1;
else if (driver_streq(argv[i], "--format") && i + 1 < argc) {
fmt = pkg_parse_format(argv[++i]);
if (fmt == PKG_FMT_AUTO) {
@@ -1020,11 +1040,12 @@ static int pkg_verify_or_unpack(DriverEnv* env, const CfreeContext* ctx,
if (fmt == PKG_FMT_AUTO) fmt = pkg_infer_format(file);
if (fmt == PKG_FMT_TARGZ)
return pkg_verify_portable(env, ctx, file, pubkey, tofu,
- unpack ? dir : NULL, unpack) == DIST_OK
+ unpack ? dir : NULL,
+ unpack && !explicit_verify) == DIST_OK
? 0
: 1;
return pkg_verify_native(env, ctx, file, pubkey, tofu, unpack ? dir : NULL,
- unpack) == DIST_OK
+ unpack && !explicit_verify) == DIST_OK
? 0
: 1;
}
@@ -1093,6 +1114,14 @@ static int pkg_trust(DriverEnv* env, const CfreeContext* ctx, int argc,
"no trusted-keys path (set CFREE_TRUSTED_KEYS or HOME)");
return 1;
}
+ if (driver_streq(sub, "path")) {
+ if (argc > 1) {
+ driver_errf(PKG_TOOL, "trust path: unexpected argument: %s", argv[1]);
+ return 2;
+ }
+ driver_printf("%s\n", tpath);
+ return 0;
+ }
if (driver_streq(sub, "list")) {
CfreeFileData fd = {0};
if (pkg_read_file(ctx, tpath, &fd) != DIST_OK) {
diff --git a/test/pkg/run.sh b/test/pkg/run.sh
@@ -1,5 +1,5 @@
#!/bin/sh
-# Driver-level checks for portable package gzip/DEFLATE handling.
+# Driver-level checks for cfree pkg distribution formats.
set -u
@@ -16,8 +16,19 @@ fi
work=$(mktemp -d "${TMPDIR:-/tmp}/cfree-pkg-test.XXXXXX")
trap 'rm -rf "$work"' EXIT
+HOME="$work/home"
+CFREE_TRUSTED_KEYS="$work/trusted_keys"
+export HOME CFREE_TRUSTED_KEYS
+mkdir -p "$HOME" "$work/in" "$work/pkg" "$work/unpack"
+
pass=0
fail=0
+skip=0
+failures=
+
+artifacts="empty.dat one.bin payload.txt chunk64.bin chunk64p1.bin chunk3.bin"
+artifact_paths=
+reversed_artifact_paths=
ok() {
printf 'PASS %s\n' "$1"
@@ -30,63 +41,442 @@ not_ok() {
sed 's/^/ | /' "$2"
fi
fail=$((fail + 1))
+ failures="$failures $1"
+}
+
+skip_test() {
+ printf 'SKIP %s\n' "$1"
+ skip=$((skip + 1))
+}
+
+run_ok() {
+ name=$1
+ shift
+ if "$@" > "$work/$name.out" 2> "$work/$name.err"; then
+ ok "$name"
+ else
+ not_ok "$name" "$work/$name.err"
+ fi
+}
+
+run_fail() {
+ name=$1
+ shift
+ if "$@" > "$work/$name.out" 2> "$work/$name.err"; then
+ {
+ echo "command unexpectedly succeeded"
+ sed 's/^/stdout: /' "$work/$name.out"
+ } > "$work/$name.diag"
+ not_ok "$name" "$work/$name.diag"
+ else
+ ok "$name"
+ fi
+}
+
+contains() {
+ name=$1
+ file=$2
+ needle=$3
+ if grep -F "$needle" "$file" >/dev/null 2>&1; then
+ ok "$name"
+ else
+ {
+ printf 'missing text: %s\n' "$needle"
+ sed 's/^/file: /' "$file"
+ } > "$work/$name.diag"
+ not_ok "$name" "$work/$name.diag"
+ fi
+}
+
+not_contains() {
+ name=$1
+ file=$2
+ needle=$3
+ if grep -F "$needle" "$file" >/dev/null 2>&1; then
+ {
+ printf 'unexpected text: %s\n' "$needle"
+ sed 's/^/file: /' "$file"
+ } > "$work/$name.diag"
+ not_ok "$name" "$work/$name.diag"
+ else
+ ok "$name"
+ fi
+}
+
+same_file() {
+ name=$1
+ want=$2
+ got=$3
+ if cmp -s "$want" "$got"; then
+ ok "$name"
+ else
+ {
+ printf 'files differ:\n'
+ printf ' want: %s\n' "$want"
+ printf ' got: %s\n' "$got"
+ } > "$work/$name.diag"
+ not_ok "$name" "$work/$name.diag"
+ fi
}
-mkdir -p "$work/in"
-{
- i=0
- while [ "$i" -lt 4096 ]; do
- printf 'portable package deflate regression line %04d\n' "$i"
- i=$((i + 1))
+same_artifacts() {
+ name=$1
+ dir=$2
+ diag="$work/$name.diag"
+ : > "$diag"
+ good=1
+ for f in $artifacts; do
+ if ! cmp -s "$work/in/$f" "$dir/$f"; then
+ printf 'artifact differs or is missing: %s\n' "$f" >> "$diag"
+ good=0
+ fi
done
-} > "$work/in/payload.txt"
+ if [ "$good" -eq 1 ]; then
+ ok "$name"
+ else
+ not_ok "$name" "$diag"
+ fi
+}
-if "$CFREE" pkg keygen -o "$work/key" > "$work/keygen.out" 2> "$work/keygen.err"; then
- ok "pkg-keygen"
-else
- not_ok "pkg-keygen" "$work/keygen.err"
-fi
+have_cmd() {
+ command -v "$1" >/dev/null 2>&1
+}
-if "$CFREE" pkg create --name deflate-smoke --version 1.0.0 \
- --format tar.gz -s "$work/key.key" -o "$work/pkg.tar.gz" \
- "$work/in/payload.txt" > "$work/create.out" 2> "$work/create.err"; then
- ok "pkg-create-targz-deflate"
-else
- not_ok "pkg-create-targz-deflate" "$work/create.err"
-fi
+make_fixtures() {
+ : > "$work/in/empty.dat"
+ printf x > "$work/in/one.bin"
+ {
+ i=0
+ while [ "$i" -lt 4096 ]; do
+ printf 'portable package deflate regression line %04d\n' "$i"
+ i=$((i + 1))
+ done
+ } > "$work/in/payload.txt"
+ dd if=/dev/zero of="$work/in/chunk64.bin" bs=65536 count=1 >/dev/null 2>&1
+ dd if=/dev/zero of="$work/in/chunk64p1.bin" bs=65536 count=1 >/dev/null 2>&1
+ printf y >> "$work/in/chunk64p1.bin"
+ dd if=/dev/zero of="$work/in/chunk3.bin" bs=65536 count=3 >/dev/null 2>&1
+ printf tail >> "$work/in/chunk3.bin"
-if gzip -t "$work/pkg.tar.gz" > "$work/gzip-test.out" 2> "$work/gzip-test.err"; then
- ok "host-gzip-accepts-cfree-output"
+ artifact_paths=
+ for f in $artifacts; do
+ artifact_paths="$artifact_paths $work/in/$f"
+ done
+
+ reversed_artifact_paths=
+ for f in chunk3.bin chunk64p1.bin chunk64.bin payload.txt one.bin empty.dat; do
+ reversed_artifact_paths="$reversed_artifact_paths $work/in/$f"
+ done
+}
+
+keyid_from_keygen() {
+ sed -n 's/.*key id \([0-9a-fA-F][0-9a-fA-F]*\)).*/\1/p' "$1"
+}
+
+inspect_checks() {
+ label=$1
+ out=$2
+ contains "$label-inspect-magic" "$out" "cfree-package 2"
+ contains "$label-inspect-name" "$out" "name = matrix-test"
+ contains "$label-inspect-version" "$out" "version = 1.0.0"
+ contains "$label-inspect-desc" "$out" "description = package test matrix"
+ contains "$label-inspect-hash" "$out" "hash = blake2b-merkle-v1"
+ contains "$label-inspect-empty" "$out" "path = empty.dat"
+ contains "$label-inspect-boundary" "$out" "path = chunk64p1.bin"
+ contains "$label-inspect-blake2b" "$out" "blake2b = "
+ not_contains "$label-inspect-no-sha256" "$out" "sha256"
+}
+
+repack_targz() {
+ src=$1
+ out=$2
+ files=$(cd "$src" && find . -type f -print | sed 's#^\./##' | sort)
+ (cd "$src" && tar -czf "$out" $files)
+}
+
+extract_targz() {
+ pkg=$1
+ dst=$2
+ mkdir -p "$dst"
+ gzip -dc "$pkg" | tar -xf - -C "$dst"
+}
+
+flip_byte() {
+ file=$1
+ off=$2
+ printf '\377' | dd of="$file" bs=1 seek="$off" count=1 conv=notrunc >/dev/null 2>&1
+}
+
+cfpkg_field_offset() {
+ file=$1
+ idx=$2
+ perl -e '
+ use strict;
+ use warnings;
+ my ($file, $idx) = @ARGV;
+ open my $fh, "<:raw", $file or die "$file: $!";
+ seek $fh, 16 + 8 * $idx, 0 or die "seek: $!";
+ read $fh, my $b, 8;
+ die "short read" unless length($b) == 8;
+ print unpack("Q<", $b);
+ ' "$file" "$idx"
+}
+
+run_matrix_for_package() {
+ label=$1
+ pkg=$2
+ format=$3
+
+ run_ok "$label-inspect" "$CFREE" pkg inspect "$pkg"
+ inspect_checks "$label" "$work/$label-inspect.out"
+
+ run_ok "$label-verify-pubkey" "$CFREE" pkg verify -p "$work/key.pub" "$pkg"
+ contains "$label-verify-output-name" "$work/$label-verify-pubkey.out" "ok: matrix-test 1.0.0"
+
+ outdir="$work/unpack/$label"
+ mkdir -p "$outdir"
+ run_ok "$label-unpack-pubkey" "$CFREE" pkg unpack --verify -p "$work/key.pub" "$pkg" -C "$outdir"
+ contains "$label-unpack-verify-output-name" "$work/$label-unpack-pubkey.out" "ok: matrix-test 1.0.0"
+ same_artifacts "$label-unpack-content" "$outdir"
+
+ run_ok "$label-verify-format-override" "$CFREE" pkg verify -p "$work/key.pub" --format "$format" "$pkg"
+}
+
+make_fixtures
+
+run_ok "pkg-keygen" "$CFREE" pkg keygen -o "$work/key"
+keyid=$(keyid_from_keygen "$work/pkg-keygen.out")
+if [ -n "$keyid" ]; then
+ ok "pkg-keygen-keyid"
else
- not_ok "host-gzip-accepts-cfree-output" "$work/gzip-test.err"
+ echo "could not parse key id from keygen output" > "$work/pkg-keygen-keyid.diag"
+ not_ok "pkg-keygen-keyid" "$work/pkg-keygen-keyid.diag"
fi
-if "$CFREE" pkg verify -p "$work/key.pub" "$work/pkg.tar.gz" \
- > "$work/verify.out" 2> "$work/verify.err"; then
- ok "pkg-verify-cfree-gzip"
+run_ok "pkg-create-targz-deflate" "$CFREE" pkg create \
+ --name matrix-test --version 1.0.0 --desc "package test matrix" \
+ --format tar.gz -s "$work/key.key" -o "$work/pkg/matrix.tar.gz" \
+ $artifact_paths
+
+run_ok "pkg-create-cfpkg-none" "$CFREE" pkg create \
+ --name matrix-test --version 1.0.0 --desc "package test matrix" \
+ --format cfpkg --compression none -s "$work/key.key" \
+ -o "$work/pkg/matrix-none.cfpkg" $artifact_paths
+
+run_ok "pkg-create-cfpkg-lz4" "$CFREE" pkg create \
+ --name matrix-test --version 1.0.0 --desc "package test matrix" \
+ --format cfpkg --compression lz4-block-v1 -s "$work/key.key" \
+ -o "$work/pkg/matrix-lz4.cfpkg" $artifact_paths
+
+run_ok "pkg-create-order-targz-forward" "$CFREE" pkg create \
+ --name order-test --version 1.0.0 --desc "order independence" \
+ --format tar.gz -s "$work/key.key" -o "$work/pkg/order-forward.tar.gz" \
+ $artifact_paths
+run_ok "pkg-create-order-targz-reversed" "$CFREE" pkg create \
+ --name order-test --version 1.0.0 --desc "order independence" \
+ --format tar.gz -s "$work/key.key" -o "$work/pkg/order-reversed.tar.gz" \
+ $reversed_artifact_paths
+run_ok "pkg-inspect-order-targz-forward" "$CFREE" pkg inspect "$work/pkg/order-forward.tar.gz"
+run_ok "pkg-inspect-order-targz-reversed" "$CFREE" pkg inspect "$work/pkg/order-reversed.tar.gz"
+same_file "pkg-order-targz-manifest-independent" \
+ "$work/pkg-inspect-order-targz-forward.out" \
+ "$work/pkg-inspect-order-targz-reversed.out"
+same_file "pkg-order-targz-bytes-independent" \
+ "$work/pkg/order-forward.tar.gz" "$work/pkg/order-reversed.tar.gz"
+
+run_ok "pkg-create-order-cfpkg-forward" "$CFREE" pkg create \
+ --name order-test --version 1.0.0 --desc "order independence" \
+ --format cfpkg --compression lz4-block-v1 -s "$work/key.key" \
+ -o "$work/pkg/order-forward.cfpkg" $artifact_paths
+run_ok "pkg-create-order-cfpkg-reversed" "$CFREE" pkg create \
+ --name order-test --version 1.0.0 --desc "order independence" \
+ --format cfpkg --compression lz4-block-v1 -s "$work/key.key" \
+ -o "$work/pkg/order-reversed.cfpkg" $reversed_artifact_paths
+run_ok "pkg-inspect-order-cfpkg-forward" "$CFREE" pkg inspect "$work/pkg/order-forward.cfpkg"
+run_ok "pkg-inspect-order-cfpkg-reversed" "$CFREE" pkg inspect "$work/pkg/order-reversed.cfpkg"
+same_file "pkg-order-cfpkg-manifest-independent" \
+ "$work/pkg-inspect-order-cfpkg-forward.out" \
+ "$work/pkg-inspect-order-cfpkg-reversed.out"
+same_file "pkg-order-cfpkg-bytes-independent" \
+ "$work/pkg/order-forward.cfpkg" "$work/pkg/order-reversed.cfpkg"
+
+run_matrix_for_package "targz" "$work/pkg/matrix.tar.gz" "tar.gz"
+run_matrix_for_package "cfpkg-none" "$work/pkg/matrix-none.cfpkg" "cfpkg"
+run_matrix_for_package "cfpkg-lz4" "$work/pkg/matrix-lz4.cfpkg" "cfpkg"
+
+run_ok "host-gzip-accepts-cfree-output" gzip -t "$work/pkg/matrix.tar.gz"
+if gunzip -c "$work/pkg/matrix.tar.gz" > "$work/pkg/matrix.tar" 2> "$work/gunzip.err" &&
+ gzip -c "$work/pkg/matrix.tar" > "$work/pkg/host.tar.gz" 2> "$work/regzip.err"; then
+ run_ok "pkg-inspect-host-gzip" "$CFREE" pkg inspect "$work/pkg/host.tar.gz"
+ run_ok "pkg-verify-host-gzip" "$CFREE" pkg verify -p "$work/key.pub" "$work/pkg/host.tar.gz"
else
- not_ok "pkg-verify-cfree-gzip" "$work/verify.err"
+ not_ok "pkg-inspect-host-gzip" "$work/gunzip.err"
+ not_ok "pkg-verify-host-gzip" "$work/regzip.err"
fi
-if gunzip -c "$work/pkg.tar.gz" > "$work/pkg.tar" 2> "$work/gunzip.err" &&
- gzip -c "$work/pkg.tar" > "$work/host.tar.gz" 2> "$work/regzip.err" &&
- "$CFREE" pkg inspect "$work/host.tar.gz" \
- > "$work/inspect-host.out" 2> "$work/inspect-host.err"; then
- ok "pkg-inspect-host-gzip"
+run_ok "pkg-create-infer-targz" "$CFREE" pkg create \
+ --name matrix-test --version 1.0.0 -s "$work/key.key" \
+ -o "$work/pkg/infer.tar.gz" "$work/in/payload.txt"
+run_ok "pkg-create-infer-cfpkg" "$CFREE" pkg create \
+ --name matrix-test --version 1.0.0 -s "$work/key.key" \
+ -o "$work/pkg/infer.cfpkg" "$work/in/payload.txt"
+run_fail "pkg-create-unknown-extension-needs-format" "$CFREE" pkg create \
+ --name matrix-test --version 1.0.0 -s "$work/key.key" \
+ -o "$work/pkg/unknown.pkg" "$work/in/payload.txt"
+run_ok "pkg-create-override-targz-extension" "$CFREE" pkg create \
+ --name matrix-test --version 1.0.0 --format tar.gz -s "$work/key.key" \
+ -o "$work/pkg/override.pkg" "$work/in/payload.txt"
+run_ok "pkg-verify-override-targz-extension" "$CFREE" pkg verify -p "$work/key.pub" \
+ --format tar.gz "$work/pkg/override.pkg"
+run_fail "pkg-verify-wrong-format-native" "$CFREE" pkg verify -p "$work/key.pub" \
+ --format cfpkg "$work/pkg/matrix.tar.gz"
+run_fail "pkg-verify-wrong-format-portable" "$CFREE" pkg verify -p "$work/key.pub" \
+ --format tar.gz "$work/pkg/matrix-none.cfpkg"
+
+run_fail "pkg-verify-untrusted-no-key" "$CFREE" pkg verify "$work/pkg/matrix.tar.gz"
+run_ok "pkg-trust-path" "$CFREE" pkg trust path
+contains "pkg-trust-path-output" "$work/pkg-trust-path.out" "$CFREE_TRUSTED_KEYS"
+run_ok "pkg-trust-list-empty" "$CFREE" pkg trust list
+contains "pkg-trust-list-empty-message" "$work/pkg-trust-list-empty.out" "(no trusted keys"
+run_ok "pkg-trust-add" "$CFREE" pkg trust add "$work/key.pub" matrix-label
+run_ok "pkg-trust-list-added" "$CFREE" pkg trust list
+contains "pkg-trust-list-added-key" "$work/pkg-trust-list-added.out" "$keyid"
+contains "pkg-trust-list-added-label" "$work/pkg-trust-list-added.out" "matrix-label"
+run_ok "pkg-verify-trusted-store" "$CFREE" pkg verify "$work/pkg/matrix.tar.gz"
+run_ok "pkg-trust-remove" "$CFREE" pkg trust remove "$keyid"
+run_ok "pkg-trust-list-removed" "$CFREE" pkg trust list
+not_contains "pkg-trust-list-removed-key" "$work/pkg-trust-list-removed.out" "$keyid"
+run_fail "pkg-verify-after-trust-remove" "$CFREE" pkg verify "$work/pkg/matrix.tar.gz"
+
+mkdir -p "$work/tofu-home"
+run_ok "pkg-verify-tofu-pins" env HOME="$work/tofu-home" CFREE_TRUSTED_KEYS="$work/tofu.keys" \
+ "$CFREE" pkg verify --tofu "$work/pkg/matrix.tar.gz"
+contains "pkg-tofu-file-key" "$work/tofu.keys" "$keyid"
+run_ok "pkg-verify-tofu-store" env HOME="$work/tofu-home" CFREE_TRUSTED_KEYS="$work/tofu.keys" \
+ "$CFREE" pkg verify "$work/pkg/matrix.tar.gz"
+
+run_ok "pkg-keygen-wrong" "$CFREE" pkg keygen -o "$work/wrong"
+run_fail "pkg-verify-wrong-pubkey" "$CFREE" pkg verify -p "$work/wrong.pub" "$work/pkg/matrix.tar.gz"
+
+if have_cmd tar; then
+ extract_targz "$work/pkg/matrix.tar.gz" "$work/tar-ok"
+ for f in cfree/package.manifest cfree/package.manifest.minisig cfree/package.pub $artifacts; do
+ safe_f=$(printf '%s\n' "$f" | sed 's#[^A-Za-z0-9_.-]#-#g')
+ if [ -f "$work/tar-ok/$f" ]; then
+ ok "portable-member-$f"
+ else
+ echo "missing tar member: $f" > "$work/portable-member-$safe_f.diag"
+ not_ok "portable-member-$f" "$work/portable-member-$safe_f.diag"
+ fi
+ done
+
+ cp -R "$work/tar-ok" "$work/tar-missing-manifest"
+ rm -f "$work/tar-missing-manifest/cfree/package.manifest"
+ repack_targz "$work/tar-missing-manifest" "$work/pkg/bad-missing-manifest.tar.gz"
+ run_fail "portable-missing-manifest-fails" "$CFREE" pkg verify -p "$work/key.pub" \
+ "$work/pkg/bad-missing-manifest.tar.gz"
+
+ cp -R "$work/tar-ok" "$work/tar-missing-signature"
+ rm -f "$work/tar-missing-signature/cfree/package.manifest.minisig"
+ repack_targz "$work/tar-missing-signature" "$work/pkg/bad-missing-signature.tar.gz"
+ run_fail "portable-missing-signature-fails" "$CFREE" pkg verify -p "$work/key.pub" \
+ "$work/pkg/bad-missing-signature.tar.gz"
+
+ cp -R "$work/tar-ok" "$work/tar-missing-artifact"
+ rm -f "$work/tar-missing-artifact/payload.txt"
+ repack_targz "$work/tar-missing-artifact" "$work/pkg/bad-missing-artifact.tar.gz"
+ run_fail "portable-missing-artifact-fails" "$CFREE" pkg verify -p "$work/key.pub" \
+ "$work/pkg/bad-missing-artifact.tar.gz"
+
+ cp -R "$work/tar-ok" "$work/tar-extra-member"
+ printf extra > "$work/tar-extra-member/extra.dat"
+ repack_targz "$work/tar-extra-member" "$work/pkg/bad-extra-member.tar.gz"
+ run_fail "portable-extra-member-fails" "$CFREE" pkg verify -p "$work/key.pub" \
+ "$work/pkg/bad-extra-member.tar.gz"
+
+ cp -R "$work/tar-ok" "$work/tar-mutated-artifact"
+ printf tamper >> "$work/tar-mutated-artifact/payload.txt"
+ repack_targz "$work/tar-mutated-artifact" "$work/pkg/bad-mutated-artifact.tar.gz"
+ run_fail "portable-mutated-artifact-fails" "$CFREE" pkg verify -p "$work/key.pub" \
+ "$work/pkg/bad-mutated-artifact.tar.gz"
+
+ cp -R "$work/tar-ok" "$work/tar-mutated-manifest"
+ printf '\n# tamper\n' >> "$work/tar-mutated-manifest/cfree/package.manifest"
+ repack_targz "$work/tar-mutated-manifest" "$work/pkg/bad-mutated-manifest.tar.gz"
+ run_fail "portable-mutated-manifest-fails" "$CFREE" pkg verify -p "$work/key.pub" \
+ "$work/pkg/bad-mutated-manifest.tar.gz"
else
- not_ok "pkg-inspect-host-gzip" "$work/inspect-host.err"
+ skip_test "portable-tar-member-and-repack-tests"
fi
-if "$CFREE" pkg verify -p "$work/key.pub" "$work/host.tar.gz" \
- > "$work/verify-host.out" 2> "$work/verify-host.err"; then
- ok "pkg-verify-host-gzip"
+cp "$work/pkg/matrix.tar.gz" "$work/pkg/bad-gzip-byte.tar.gz"
+flip_byte "$work/pkg/bad-gzip-byte.tar.gz" 0
+run_fail "portable-corrupt-gzip-fails" "$CFREE" pkg verify -p "$work/key.pub" \
+ "$work/pkg/bad-gzip-byte.tar.gz"
+
+cp "$work/pkg/matrix-none.cfpkg" "$work/pkg/bad-native-magic.cfpkg"
+flip_byte "$work/pkg/bad-native-magic.cfpkg" 0
+run_fail "native-bad-magic-fails" "$CFREE" pkg verify -p "$work/key.pub" \
+ "$work/pkg/bad-native-magic.cfpkg"
+
+dd if="$work/pkg/matrix-none.cfpkg" of="$work/pkg/bad-native-truncated.cfpkg" \
+ bs=1 count=100 >/dev/null 2>&1
+run_fail "native-truncated-fails" "$CFREE" pkg verify -p "$work/key.pub" \
+ "$work/pkg/bad-native-truncated.cfpkg"
+
+cp "$work/pkg/matrix-none.cfpkg" "$work/pkg/bad-native-manifest.cfpkg"
+flip_byte "$work/pkg/bad-native-manifest.cfpkg" 160
+run_fail "native-mutated-manifest-fails" "$CFREE" pkg verify -p "$work/key.pub" \
+ "$work/pkg/bad-native-manifest.cfpkg"
+
+if have_cmd perl; then
+ descriptor_off=$(cfpkg_field_offset "$work/pkg/matrix-none.cfpkg" 4)
+ index_off=$(cfpkg_field_offset "$work/pkg/matrix-none.cfpkg" 10)
+ content_off=$(cfpkg_field_offset "$work/pkg/matrix-none.cfpkg" 12)
+
+ cp "$work/pkg/matrix-none.cfpkg" "$work/pkg/bad-native-descriptor.cfpkg"
+ flip_byte "$work/pkg/bad-native-descriptor.cfpkg" "$descriptor_off"
+ run_fail "native-mutated-descriptor-fails" "$CFREE" pkg verify -p "$work/key.pub" \
+ "$work/pkg/bad-native-descriptor.cfpkg"
+
+ cp "$work/pkg/matrix-none.cfpkg" "$work/pkg/bad-native-index.cfpkg"
+ flip_byte "$work/pkg/bad-native-index.cfpkg" "$index_off"
+ run_fail "native-mutated-index-fails" "$CFREE" pkg verify -p "$work/key.pub" \
+ "$work/pkg/bad-native-index.cfpkg"
+
+ cp "$work/pkg/matrix-none.cfpkg" "$work/pkg/bad-native-content.cfpkg"
+ flip_byte "$work/pkg/bad-native-content.cfpkg" "$content_off"
+ run_fail "native-mutated-content-fails" "$CFREE" pkg verify -p "$work/key.pub" \
+ "$work/pkg/bad-native-content.cfpkg"
else
- not_ok "pkg-verify-host-gzip" "$work/verify-host.err"
+ skip_test "native-region-offset-corruption-tests"
fi
+run_fail "pkg-create-missing-required-fails" "$CFREE" pkg create \
+ --name missing-version -s "$work/key.key" -o "$work/pkg/missing.tar.gz" "$work/in/payload.txt"
+run_fail "pkg-create-unknown-format-fails" "$CFREE" pkg create \
+ --name matrix-test --version 1.0.0 --format nope -s "$work/key.key" \
+ -o "$work/pkg/badformat.tar.gz" "$work/in/payload.txt"
+run_fail "pkg-create-unknown-compression-fails" "$CFREE" pkg create \
+ --name matrix-test --version 1.0.0 --format cfpkg --compression nope \
+ -s "$work/key.key" -o "$work/pkg/badcomp.cfpkg" "$work/in/payload.txt"
+mkdir -p "$work/dup/a" "$work/dup/b"
+printf one > "$work/dup/a/same.dat"
+printf two > "$work/dup/b/same.dat"
+run_fail "pkg-create-duplicate-artifact-path-fails" "$CFREE" pkg create \
+ --name matrix-test --version 1.0.0 --format tar.gz -s "$work/key.key" \
+ -o "$work/pkg/duplicate.tar.gz" "$work/dup/a/same.dat" "$work/dup/b/same.dat"
+run_fail "pkg-verify-missing-file-fails" "$CFREE" pkg verify -p "$work/key.pub" \
+ "$work/pkg/does-not-exist.tar.gz"
+printf not-a-package > "$work/not-a-package.cfpkg"
+run_fail "pkg-inspect-malformed-fails" "$CFREE" pkg inspect "$work/not-a-package.cfpkg"
+run_fail "pkg-trust-add-missing-pubkey-fails" "$CFREE" pkg trust add
+run_fail "pkg-trust-remove-bad-keyid-fails" "$CFREE" pkg trust remove xyz
+
if [ "$fail" -ne 0 ]; then
- printf 'pkg: %d passed, %d failed\n' "$pass" "$fail"
+ printf 'pkg: failures:%s\n' "$failures"
+ printf 'pkg: %d passed, %d failed, %d skipped\n' "$pass" "$fail" "$skip"
exit 1
fi
-printf 'pkg: %d passed\n' "$pass"
+printf 'pkg: %d passed, %d skipped\n' "$pass" "$skip"