#!/usr/bin/env bash set -euo pipefail usage() { cat <<'EOF' Usage: verify_deb.sh FILE.deb [EXPECTED_VERSION] [EXPECTED_ARCH] Prints package metadata, key content listing, and SHA-256. When an expected version and/or architecture is given, mismatches fail with a non-zero exit. EOF } if [[ $# -lt 1 || $# -gt 3 ]]; then usage >&2 exit 2 fi if [[ "$1" == "-h" || "$1" == "--help" ]]; then usage exit 0 fi package=$1 expected_version=${2:-} expected_arch=${3:-} fail=0 if [[ ! -f "$package" ]]; then echo "Error: file not found: $package" >&2 exit 2 fi if [[ ! -s "$package" ]]; then echo "Error: empty file: $package" >&2 exit 2 fi if ! command -v dpkg-deb >/dev/null 2>&1; then echo "Error: dpkg-deb is required." >&2 exit 2 fi echo "== metadata ==" info=$(dpkg-deb --info "$package") || { echo "Error: dpkg-deb --info failed; not a valid Debian package." >&2 exit 1 } printf '%s\n' "$info" package_name=$(dpkg-deb --field "$package" Package 2>/dev/null || true) package_version=$(dpkg-deb --field "$package" Version 2>/dev/null || true) package_arch=$(dpkg-deb --field "$package" Architecture 2>/dev/null || true) # Debian versions never start with 'v'; git tags usually do. Compare normalized. expected_version=${expected_version#v} if [[ -z "$package_name" || -z "$package_version" || -z "$package_arch" ]]; then echo "FAIL: missing Package/Version/Architecture field." >&2 fail=1 fi if [[ -n "$expected_version" && "$package_version" != "$expected_version" ]]; then echo "FAIL: version mismatch: expected $expected_version, got $package_version" >&2 fail=1 fi if [[ -n "$expected_arch" && "$package_arch" != "$expected_arch" ]]; then echo "FAIL: architecture mismatch: expected $expected_arch, got $package_arch" >&2 fail=1 fi # Filename shape per contract: __.deb base=$(basename -- "$package") if [[ ! "$base" =~ ^[^_]+_[^_]+_[^_]+\.deb$ ]]; then echo "FAIL: filename does not match __.deb: $base" >&2 fail=1 elif [[ -n "$package_version" && ! "$base" == *"${package_version}"* ]]; then echo "FAIL: filename version does not match package Version ($package_version): $base" >&2 fail=1 fi echo "== contents (top level + binaries) ==" dpkg-deb --contents "$package" | sed -n '1,40p' echo "== maintainer scripts permissions (when present) ==" control_dir=$(mktemp -d) trap 'rm -rf -- "$control_dir"' EXIT if dpkg-deb --control "$package" "$control_dir" 2>/dev/null; then found_scripts=false for script in preinst postinst prerm postrm; do if [[ -f "$control_dir/$script" ]]; then found_scripts=true mode=$(stat -c '%a' "$control_dir/$script") if [[ $mode =~ .*[2367]$ ]]; then echo "OK: $script mode $mode" else echo "FAIL: $script not executable (mode $mode)" >&2 fail=1 fi fi done if [[ "$found_scripts" == false ]]; then echo "(no maintainer scripts)" fi fi echo "== sha256 ==" sha256sum "$package" if ((fail > 0)); then echo "VERIFY: FAILED" >&2 exit 1 fi echo "VERIFY: OK"