From d12c5f78087be749c25833cb702b508749a1335b Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Thu, 16 Jul 2026 19:24:49 -0500 Subject: [PATCH] test(mosaic): make #791 PR2 Part 7 symlink control busybox-portable (#791 PR2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI #1881 failed at head 8bee1b65: the durable-snapshot gate was 40/41, the sole failure being the Part 7 NEGATIVE control ("secret leaked through the symlink"). Root cause is a test-harness portability gap, not a code defect: node:24-alpine runs busybox cp as root, and busybox cp REPLACES a symlinked destination instead of following it, whereas GNU/BSD cp — what a real operator runs `mosaic update` under — follows the link and leaks. So under the CI harness the CWE-59 leak vector the control asserts simply cannot occur, and the negative control can't reproduce. Fix is test-only; install.sh (independently approved at 8bee1b65) and the real security assertions are untouched. make_symlink_leaf_shim now emulates GNU cp's follow-through-dest-symlink behavior portably: when the destination is a symlink it writes the source bytes through the link via redirection (which follows symlinks on every coreutils, busybox included); otherwise it delegates to the host cp unchanged. Both the shipped-case and the control run through this single shim, so the ONLY difference between them remains the SYMLINK-LEAF-GUARD — the control stays load-bearing and non-tautological, now on busybox too. With the guard present the symlinked leaf is dropped before this cp runs, so the shipped path is unchanged. Verified in the exact CI image (node:24-alpine, busybox, root, apk add bash rsync): durable-snapshot 41/41, manifest-guard 193, rollback 28. GNU host 41/41, shellcheck clean. Sole tracked delta vs 8bee1b65 = this test file. Co-Authored-By: Claude Opus 4.8 --- .../scripts/test-upgrade-durable-snapshot.sh | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/mosaic/framework/tools/quality/scripts/test-upgrade-durable-snapshot.sh b/packages/mosaic/framework/tools/quality/scripts/test-upgrade-durable-snapshot.sh index ef58dab..3cf8294 100644 --- a/packages/mosaic/framework/tools/quality/scripts/test-upgrade-durable-snapshot.sh +++ b/packages/mosaic/framework/tools/quality/scripts/test-upgrade-durable-snapshot.sh @@ -95,17 +95,38 @@ seed_home_v1() { # sync), so the verify net must restore a REAL file in place WITHOUT following the # link (which would write the snapshot's secret out through it). $EXFIL_TARGET is # expanded at shim-write time from the caller's environment. +# +# PORTABILITY (why this shim, not the real `cp`): the CWE-59 leak this exercises is +# `cp` writing THROUGH a symlinked destination. GNU/BSD cp — what a real operator +# runs `mosaic update` under — follows the dest symlink and leaks. busybox cp (the +# Alpine CI image) REPLACES a symlinked dest instead of following it, so under the +# CI harness the leak vector simply does not exist and the negative control could +# never reproduce it. This shim therefore emulates the real-target GNU cp behavior +# PORTABLY: when the destination is a symlink it writes the source bytes through the +# link via redirection (which follows symlinks on every coreutils, busybox included); +# otherwise it delegates to the host's real cp unchanged. Both the shipped-case and +# the negative control run through this identical shim, so the ONLY difference +# between them remains the SYMLINK-LEAF-GUARD — the control stays load-bearing and +# non-tautological. It does NOT touch install.sh (approved) or the real assertions: +# with the guard present the symlinked leaf is dropped BEFORE this cp runs, so the +# dest is a real file and the delegate path is taken exactly as on a GNU host. make_symlink_leaf_shim() { local dir="$1" home="$2" cat > "$dir/cp" < "\$dest" + exit \$? +fi exec env PATH="$ORIG_PATH" cp "\$@" SHIM chmod +x "$dir/cp"