Files
live_recorder/scripts/verify-fnos-package.sh
T

125 lines
4.8 KiB
Bash
Executable File

#!/bin/bash
set -euo pipefail
PACKAGE=${1:?usage: verify-fnos-package.sh package.fpk}
VERIFY_TMP_ROOT="${LIVERECORDER_VERIFY_TMPDIR:-${TMPDIR:-/tmp}}"
MAX_APP_UNCOMPRESSED_BYTES=$((512 * 1024 * 1024))
mkdir -p "$VERIFY_TMP_ROOT"
WORK_DIR=$(mktemp -d "${VERIFY_TMP_ROOT%/}/fnos-package-verify.XXXXXX")
trap 'rm -rf -- "$WORK_DIR"' EXIT
manifest_value() {
sed -n "s/^$1[[:space:]]*=[[:space:]]*//p" "$WORK_DIR/manifest" | head -n 1 | tr -d '\r'
}
tar -xzf "$PACKAGE" -C "$WORK_DIR"
appname=$(manifest_value appname)
version=$(manifest_value version)
case "$appname" in
liverecorder|nxsir.postgresql) ;;
*) printf 'unexpected fnOS appname: %s\n' "$appname" >&2; exit 1 ;;
esac
test -n "$version"
test "$(manifest_value platform)" = "x86"
test -x "$WORK_DIR/cmd/main"
test -x "$WORK_DIR/cmd/install_callback"
test -s "$WORK_DIR/wizard/install"
test -s "$WORK_DIR/wizard/upgrade"
test -s "$WORK_DIR/ICON.PNG"
test -s "$WORK_DIR/ICON_256.PNG"
expected=$(manifest_value checksum)
actual=$(md5sum "$WORK_DIR/app.tgz" | cut -d' ' -f1)
test -n "$expected"
test "$expected" = "$actual"
gzip -t "$WORK_DIR/app.tgz"
gzip -dc "$WORK_DIR/app.tgz" >"$WORK_DIR/app.tar"
uncompressed_bytes=$(wc -c <"$WORK_DIR/app.tar")
if [ "$uncompressed_bytes" -gt "$MAX_APP_UNCOMPRESSED_BYTES" ]; then
printf 'app.tgz expands to %s bytes; limit is %s bytes\n' \
"$uncompressed_bytes" "$MAX_APP_UNCOMPRESSED_BYTES" >&2
exit 1
fi
tar -tf "$WORK_DIR/app.tar" >"$WORK_DIR/app-files.txt"
if awk '/^\// || /(^|\/)\.\.($|\/)/ { unsafe = 1; exit } END { exit unsafe ? 0 : 1 }' "$WORK_DIR/app-files.txt"; then
printf 'app.tgz contains an unsafe member path\n' >&2
exit 1
fi
tar -tvf "$WORK_DIR/app.tar" >"$WORK_DIR/app-metadata.txt"
if awk '
/^l/ {
marker = " -> "
offset = index($0, marker)
if (offset > 0) {
target = substr($0, offset + length(marker))
if (target ~ /^\//) { unsafe = 1; exit }
}
}
/^h/ {
marker = " link to "
offset = index($0, marker)
if (offset > 0) {
target = substr($0, offset + length(marker))
if (target ~ /^\//) { unsafe = 1; exit }
}
}
END { exit unsafe ? 0 : 1 }
' "$WORK_DIR/app-metadata.txt"; then
printf 'app.tgz contains an unsafe symbolic or hard link\n' >&2
exit 1
fi
grep -q '^server/wwwroot/index.html$' "$WORK_DIR/app-files.txt"
grep -q '^runtime/usr/lib/postgresql/15/bin/postgres$' "$WORK_DIR/app-files.txt"
grep -q '^runtime/usr/lib/postgresql/15/bin/initdb$' "$WORK_DIR/app-files.txt"
grep -q '^runtime/usr/lib/postgresql/15/bin/pg_ctl$' "$WORK_DIR/app-files.txt"
grep -q '^runtime/usr/share/postgresql/15/postgresql.conf.sample$' "$WORK_DIR/app-files.txt"
grep -q '^ui/config$' "$WORK_DIR/app-files.txt"
grep -q '^ui/images/icon_64.png$' "$WORK_DIR/app-files.txt"
if [ "$appname" = "liverecorder" ]; then
grep -q '^server/LiveRecorder.WebApi$' "$WORK_DIR/app-files.txt"
grep -q '^server/Platforms/Douyin/Signing/sign-xbogus.js$' "$WORK_DIR/app-files.txt"
grep -q '^runtime/bin/node$' "$WORK_DIR/app-files.txt"
grep -q '^runtime/bin/curl$' "$WORK_DIR/app-files.txt"
grep -q '^runtime/etc/ssl/certs/ca-certificates.crt$' "$WORK_DIR/app-files.txt"
else
grep -q '^server/PostgresService.WebApi$' "$WORK_DIR/app-files.txt"
grep -q '^runtime/usr/lib/postgresql/15/lib/vector.so$' "$WORK_DIR/app-files.txt"
grep -q '^runtime/usr/share/postgresql/15/extension/vector.control$' "$WORK_DIR/app-files.txt"
fi
if grep -Eq '^runtime/.*/(ffmpeg|ffprobe)$' "$WORK_DIR/app-files.txt"; then
printf 'ffmpeg and ffprobe must come from the fnOS system environment\n' >&2
exit 1
fi
if grep -Eq '^runtime/lib/(ld-linux-.*|libc\.so\..*|libBrokenLocale\.so\..*|libanl\.so\..*|libdl\.so\..*|libm(vec)?\.so\..*|libnss_(compat|dns|files|hesiod)\.so\..*|libpthread\.so\..*|libresolv\.so\..*|librt\.so\..*|libthread_db\.so\..*|libutil\.so\..*)$' "$WORK_DIR/app-files.txt"; then
printf 'the FPK must use the fnOS glibc and matching system loader\n' >&2
exit 1
fi
if grep -Eq '^(data|records|postgres|log|var)/' "$WORK_DIR/app-files.txt"; then
printf 'persistent data must not be included in app.tgz\n' >&2
exit 1
fi
mkdir -p "$WORK_DIR/app"
tar -xf "$WORK_DIR/app.tar" -C "$WORK_DIR/app"
while IFS= read -r -d '' link_path; do
target=$(readlink -- "$link_path")
case "$target" in
/*) printf 'absolute symbolic link in app.tgz: %s -> %s\n' "$link_path" "$target" >&2; exit 1 ;;
esac
resolved=$(realpath -m -- "$(dirname -- "$link_path")/$target")
case "$resolved" in
"$WORK_DIR/app"/*) ;;
*) printf 'escaping symbolic link in app.tgz: %s -> %s\n' "$link_path" "$target" >&2; exit 1 ;;
esac
done < <(find "$WORK_DIR/app" -type l -print0)
printf 'fnOS package verified: %s (%s bytes uncompressed)\n' "$PACKAGE" "$uncompressed_bytes"