#!/bin/bash
# NovaGuard macOS diagnostics collector (read-only).
# It only reads information and copies logs; nothing is installed, changed or removed.
#
# Usage:  sudo bash NovaGuard_collect_diagnostics.sh "/path/to/NovaGuard_downloader_....pkg"
# Result: a .zip on the Desktop of the user who ran sudo. Please review it before sending.

if [ "$(id -u)" -ne 0 ]; then
	echo "Please run with sudo:  sudo bash $0 \"/path/to/installer.pkg\""
	exit 1
fi

PKG="$1"
REAL_USER="${SUDO_USER:-$(stat -f %Su /dev/console)}"
USER_HOME="$(eval echo ~"$REAL_USER")"
DESKTOP="$USER_HOME/Desktop"
# No path given: fall back to NovaGuard installers in the user's Downloads folder
if [ -z "$PKG" ] || [ ! -f "$PKG" ]; then
	echo "WARNING: no valid .pkg path given ('$PKG'). Looking for NovaGuard*.pkg in $USER_HOME/Downloads ..."
	PKG=""
fi
OUT="$DESKTOP/NovaGuard_diag_$(hostname -s)_$(date +%Y%m%d_%H%M%S)"
AGENT_DIR="/Library/sf/edr/agent"
mkdir -p "$OUT"
echo "Collecting into $OUT ..."

# 1. System and Rosetta
{
	sw_vers
	echo "Hardware arch: $(uname -m)"
	sysctl -n machdep.cpu.brand_string
	if arch -x86_64 /usr/bin/true 2>/dev/null; then echo "Rosetta: installed"; else echo "Rosetta: NOT installed"; fi
	echo "SIP: $(csrutil status 2>&1)"
	echo "Gatekeeper: $(spctl --status 2>&1)"
} > "$OUT/01_system.txt" 2>&1

# 2. Installer package signature (as macOS itself evaluates it)
{
	if [ -n "$PKG" ]; then
		PKGS=("$PKG")
	else
		PKGS=()
		while IFS= read -r p; do PKGS+=("$p"); done < <(find "$USER_HOME/Downloads" -maxdepth 2 -iname "NovaGuard*.pkg" 2>/dev/null)
	fi
	if [ ${#PKGS[@]} -eq 0 ]; then
		echo "No installer .pkg found. Re-run with the path to the .pkg used for deployment."
	fi
	for p in "${PKGS[@]}"; do
		echo "########## $p"
		echo "### shasum -a 256"; shasum -a 256 "$p"
		echo; echo "### pkgutil --check-signature"; pkgutil --check-signature "$p"
		echo; echo "### spctl -a -vvv -t install"; spctl -a -vvv -t install "$p"
		# stapler needs Xcode tools; only use it if they are already installed (avoids an install prompt)
		if xcode-select -p >/dev/null 2>&1; then
			echo; echo "### stapler validate"; xcrun stapler validate "$p"
		fi
		echo
	done
} > "$OUT/02_pkg_signature.txt" 2>&1
[ -s "$OUT/02_pkg_signature.txt" ] && grep -q "No installer .pkg found" "$OUT/02_pkg_signature.txt" && echo "WARNING: no installer .pkg found - section 02 will be empty."

# 3. Every Mach-O binary/library the agent downloaded: architecture + signature
{
	if [ -d "$AGENT_DIR" ]; then
		find "$AGENT_DIR" /Library/sf/edr/install -type f 2>/dev/null | while read -r f; do
			if file -b "$f" | grep -q "Mach-O"; then
				echo "=== $f"
				# 'file' reports the architecture(s); lipo is avoided because it triggers an Xcode tools install prompt
				echo "type:  $(file -b "$f" | tr '\n' ' ')"
				codesign -dvv "$f" 2>&1 | grep -E "Identifier=|Format=|Authority=|TeamIdentifier=|Timestamp=|flags=|not signed"
				if codesign --verify --strict "$f" >/dev/null 2>&1; then echo "verify: OK"; else echo "verify: FAILED ($(codesign --verify --strict "$f" 2>&1 | tail -1))"; fi
				xattr -l "$f" 2>/dev/null | grep -i quarantine
				echo
			fi
		done
	else
		echo "$AGENT_DIR does not exist"
	fi
} > "$OUT/03_agent_binaries.txt" 2>&1

# 3b. Entitlements of the daemon binary (hardened runtime / library validation)
{
	for f in "$AGENT_DIR/bin/edr_services" /Library/sf/edr/install/edr_services; do
		[ -f "$f" ] || continue
		echo "=== $f"; codesign -d --entitlements - "$f" 2>&1; echo
	done
} > "$OUT/03b_daemon_entitlements.txt" 2>&1

# 3c. Running NovaGuard processes
ps -axo pid,ppid,user,etime,command | grep -E "PID|/Library/sf/" | grep -v grep > "$OUT/03c_processes.txt" 2>&1

# 4. File listing and installer receipt
ls -laR /Library/sf > "$OUT/04_file_listing.txt" 2>&1
{
	pkgutil --pkgs | grep -iE "sangfor|novaguard|forenova"
	echo; pkgutil --pkg-info com.sangfor.pkg.edrinstaller
	echo; pkgutil --files com.sangfor.pkg.edrinstaller
} > "$OUT/05_pkg_receipt.txt" 2>&1

# 5. LaunchDaemon state
mkdir -p "$OUT/launchdaemons"
cp /Library/LaunchDaemons/com.sangfor.* "$OUT/launchdaemons/" 2>/dev/null
{
	for p in /Library/LaunchDaemons/com.sangfor.*; do
		[ -f "$p" ] || continue
		label=$(/usr/libexec/PlistBuddy -c "Print :Label" "$p" 2>/dev/null)
		echo "##### $p (label: $label)"
		launchctl print "system/$label" 2>&1
		echo
	done
} > "$OUT/06_launchd.txt" 2>&1

# 6. Install, pre-check and agent logs
grep -iE "sangfor|novaguard|edr|postinstall|preinstall|aes_installer" /var/log/install.log > "$OUT/07_install_log_filtered.txt" 2>&1
mkdir -p "$OUT/tmp_logs"
cp /tmp/install_log /tmp/install_pre_check_log /tmp/installlog.log "$OUT/tmp_logs/" 2>/dev/null
# LaunchDaemon stdout/stderr: shows dyld "Library not loaded" errors if the daemon fails to start
mkdir -p "$OUT/agent_startup_log"
cp "$AGENT_DIR"/var/log/edrDaemon-startup.* "$OUT/agent_startup_log/" 2>/dev/null

# 7. Crash reports for the agent
mkdir -p "$OUT/crash_reports"
find /Library/Logs/DiagnosticReports "$USER_HOME/Library/Logs/DiagnosticReports" -type f -mtime -14 2>/dev/null \
	| grep -iE "edr|sangfor|eps|novaguard" | while read -r c; do cp "$c" "$OUT/crash_reports/"; done

# 8. Unified log: dyld / code-signing rejections in the last 24h
log show --last 24h --style compact \
	--predicate 'eventMessage CONTAINS[c] "/Library/sf/" OR eventMessage CONTAINS[c] "sangfor" OR eventMessage CONTAINS[c] "libepsbase" OR eventMessage CONTAINS[c] "edr_services"' \
	> "$OUT/08_unified_log.txt" 2>&1

# 9. Intune agent logs (deployment status of the pkg)
[ -d /Library/Logs/Microsoft/Intune ] && cp -R /Library/Logs/Microsoft/Intune "$OUT/intune_logs" 2>/dev/null

chown -R "$REAL_USER" "$OUT"
ditto -c -k --sequesterRsrc --keepParent "$OUT" "$OUT.zip"
chown "$REAL_USER" "$OUT.zip"
echo "Done: $OUT.zip"
echo "Please review the contents (hostnames, usernames, paths) before sending."
