#!/bin/bash # test_nyai.sh — nyai 宿主回归(M35:app.json 清单权威 + SD 暂存 + 无条件清理) # # 覆盖: # 1 文件名/后缀与包名完全不同(weird-name.bin)仍按清单装进 MeowAPPs/ # 2 真实反例:签名 os.meow.reader 包命名成 os.meow.reader-0.1.7.mpk 仍装进 MeowAPPs/os.meow.reader # 3 footer.package ≠ app.json.package → 拒装,错误文案同时给出两个名字 # 4 os.meow.* 无签名 → 拒装 # 5 第三方无签名 → 放行(行为不变) # 6 恶意 app.json.package(../x、a/b、.、..)→ 拒装且不落盘到目录外 # 7 成功 / 各种失败 / 解压失败 / SIGTERM 中断 → MeowOS/tmp/pkgs 无残留 # 8 索引路径:Packages 的 Package 字段取自清单(文件改名后按包名仍可装) # 9 upgrade 复用 install 引擎:清单包名/目标包名不一致、降级都拒 # # 依赖:宿主 cc(编 nyai_host / meow_verify_host / meow_sign)。 # 签名用例需要维护者私钥:默认 ~/.keys/meowos-signing/signing.key, # 可用 NYAI_TEST_SIGN_KEY 覆盖;无私钥时签名用例 SKIP(不判 FAIL)。 # 真实反例包默认 ~/.cache/os.meow.reader-0.1.7-signed.mpk,可用 NYAI_TEST_REAL_MPK 覆盖。 # # 用法:./test_nyai.sh [-v] set -u HERE=$(cd "$(dirname "$0")" && pwd) ROOT=$(cd "$HERE/../.." && pwd) VERBOSE=0 [ "${1:-}" = "-v" ] && VERBOSE=1 WORK=${NYAI_TEST_WORK:-$(mktemp -d "${TMPDIR:-/tmp}/nyai-m35.XXXXXX")} mkdir -p "$WORK" CLEANUP_WORK=1 [ -n "${NYAI_TEST_WORK:-}" ] && CLEANUP_WORK=0 trap '[ "$CLEANUP_WORK" = 1 ] && rm -rf "$WORK"' EXIT PASS=0; FAIL=0; SKIP=0 ok() { PASS=$((PASS+1)); printf 'PASS %s\n' "$1"; } bad() { FAIL=$((FAIL+1)); printf 'FAIL %s\n' "$1"; [ -n "${2:-}" ] && printf ' %s\n' "$2"; } skip() { SKIP=$((SKIP+1)); printf 'SKIP %s (%s)\n' "$1" "${2:-}"; } KEY=${NYAI_TEST_SIGN_KEY:-$HOME/.keys/meowos-signing/signing.key} [ -r "$KEY" ] || KEY="" REAL_MPK=${NYAI_TEST_REAL_MPK:-$HOME/.cache/os.meow.reader-0.1.7-signed.mpk} # ---------------- 构建 ---------------- echo "== build ==" ( cd "$HERE" && ./build_nyai.sh host >/dev/null ) || { echo "build nyai_host failed"; exit 1; } NYAI="$HERE/nyai_host" CC=${CC_HOST:-cc} V="$ROOT/patch_v2/meow_verify" mkdir -p "$WORK/tools" "$CC" -Os -Wno-sign-compare -Wno-unterminated-string-initialization -Wno-unused-function \ -c "$V/tweetnacl.c" -o "$WORK/tools/tn.o" || { echo "build tweetnacl failed"; exit 1; } "$CC" -Os -c "$V/sha256.c" -o "$WORK/tools/sha.o" || exit 1 "$CC" -Os -I"$V" -c "$V/meow_verify.c" -o "$WORK/tools/mv.o" || exit 1 "$CC" "$WORK/tools/mv.o" "$WORK/tools/sha.o" "$WORK/tools/tn.o" -o "$WORK/tools/meow_verify_host" || exit 1 if [ -n "$KEY" ]; then "$CC" -Os -I"$V" -I"$ROOT/patch_v2/meow_sign" "$ROOT/patch_v2/meow_sign/meow_sign.c" \ "$WORK/tools/tn.o" "$WORK/tools/sha.o" -o "$WORK/tools/meow_sign" || exit 1 fi VERIFY="$WORK/tools/meow_verify_host" # ---------------- 辅助 ---------------- sb() { # sb → 打印沙箱 MeowROOT local d="$WORK/case-$1" rm -rf "$d" mkdir -p "$d/root/MeowAPPs" "$d/root/MeowOS/opt/nyai" printf '%s\n' "$d/root" } mkpkg() { # mkpkg rm -rf "$1"; mkdir -p "$1" printf '{"name":"Test","package":"%s","exec":"bin","version":"0.1.%s","version_code":%s}\n' \ "$2" "$3" "$3" > "$1/app.json" printf '#!/bin/sh\nexit 0\n' > "$1/bin"; chmod +x "$1/bin" } pack() { tar czf "$2" -C "$1" .; } mk_evil_tar() { # mk_evil_tar :成员名可含 ../ 或绝对路径 python3 - "$1" "$2" "$3" <<'PY' import sys, tarfile, io, gzip out, name, pkg = sys.argv[1], sys.argv[2], sys.argv[3] buf = io.BytesIO() with tarfile.open(fileobj=buf, mode='w') as t: ti = tarfile.TarInfo(name); ti.size = 3; t.addfile(ti, io.BytesIO(b'pwn')) j = ('{"name":"X","package":"%s","exec":"x","version":"1","version_code":1}' % pkg).encode() tj = tarfile.TarInfo('app.json'); tj.size = len(j); t.addfile(tj, io.BytesIO(j)) open(out, 'wb').write(gzip.compress(buf.getvalue())) PY } mk_bomb_tar() { # mk_bomb_tar :成员头声明 700MB 的解压炸弹 python3 - "$1" <<'PY' import sys, tarfile, gzip ti = tarfile.TarInfo('big.bin'); ti.size = 700 * 1024 * 1024 ti.mtime = 0; ti.mode = 0o644; ti.uid = ti.gid = 0 hdr = ti.tobuf(format=tarfile.USTAR_FORMAT) open(sys.argv[1], 'wb').write(gzip.compress(hdr + b'\0' * 1024)) PY } sign() { # sign :文件名茎必须等于包名(meow_sign 约定),签完覆盖原文件 [ -n "$KEY" ] || return 2 mkdir -p "$WORK/sign" cp "$2" "$WORK/sign/$1.mpk" "$WORK/tools/meow_sign" sign "$WORK/sign/$1.mpk" "$KEY" >/dev/null 2>&1 || return 1 cp "$WORK/sign/$1.mpk" "$2" } nyai_run() { # nyai_run local r=$1; shift MEOWROOT="$r" MEOW_VERIFY_BIN="$VERIFY" "$NYAI" "$@" } tmp_clean() { # tmp_clean :MeowOS/tmp/pkgs 下无任何条目 local n n=$(find "$1/MeowOS/tmp/pkgs" -mindepth 1 2>/dev/null | wc -l | tr -d ' ') [ "$n" = "0" ] } show() { [ "$VERBOSE" = 1 ] && printf '%s\n' "$1"; } expect() { # expect