File: //opt/imunify360/venv/share/imunify360/scripts/imunify-check-pkg-integrity
#!/bin/bash
# imunify-check-pkg-integrity: verify imunify* package file integrity
# via rpm -V / dpkg -V. Outputs results to stdout.
#
# Deployed to:
# RPM: /usr/share/imunify360/scripts/imunify-check-pkg-integrity
# DEB: /opt/imunify360/venv/share/imunify360/scripts/imunify-check-pkg-integrity
# Package: imunify-core
#
# This script is also sourced by imunify-force-update.sh for shared functions
# (filter_verify_noise, filter_whitelisted_files, INTEGRITY_WHITELIST_FILES, etc.).
# set -euo pipefail is only applied when run directly, not when sourced.
DEBIAN_VERSION_FILE="${DEBIAN_VERSION_FILE:-/etc/debian_version}"
# Files always reported as modified on a healthy system.
# On RPM most of these are %config and already filtered by filter_verify_noise;
# on Debian they are NOT marked as conffiles, so we whitelist explicitly.
# Uses substring matching (grep -v -F).
INTEGRITY_WHITELIST_FILES=(
"imunify360.config.defaults.example"
# imunify-ui-*-cpanel: triggerin runs sed to patch IMUNIFY_PACKAGE in config.js
"imunify/assets/js/config.js"
# imunify360-php-i360: postinst copies .ini/.so per PHP version from templates
"/i360.ini"
"/i360.so"
# i360-php-opts: module.ini modified during config (conffile on RPM, not on DEB)
"i360-php-opts/module.ini"
# imunify360-ossec-server: postinst sed-patches analysisd.stats_maxdiff
"internal_options.conf"
# imunify360-webshield-bundle: service updates these at runtime (conffile on RPM, not on DEB)
"imunify360-webshield/common-proxies.conf"
"imunify360-webshield/country_ips.conf"
"imunify360-webshield/whitelisted-domains.conf"
)
detect_pkg_manager() {
if [ -f "$DEBIAN_VERSION_FILE" ]; then
echo "dpkg"
elif command -v rpm >/dev/null 2>&1; then
echo "rpm"
else
echo "unknown"
fi
}
check_dpkg_verify_supported() {
dpkg -V --help >/dev/null 2>&1
}
# Filter rpm -V / dpkg -V output to keep only lines indicating real corruption.
#
# Both tools use a 9-char flag string + optional type flag + path.
# rpm: SM5DLUGTP [c|d|g|l|r| ] /path dpkg: ??5?????? [c| ] /path
# rpm also emits: missing /path
#
# Excluded:
# - config files (type 'c') and ghost files (type 'g')
# - .pyc files (Python bytecode cache, regenerated at import)
# - metadata-only changes (no S or 5 flag — just mode/group/time drift)
# Kept:
# - 'missing' lines for non-pyc files
# - lines with S (size, pos 1) or 5 (digest, pos 3) change
filter_verify_noise() {
grep -v -E '^.{9} +[cg] ' \
| grep -v '\.pyc$' \
| grep -E '^(S|..5|[?][?]5|missing)' \
|| true
}
filter_whitelisted_files() {
local input
input=$(cat)
if [[ ${#INTEGRITY_WHITELIST_FILES[@]} -eq 0 ]]; then
echo "$input"
return
fi
local pattern=""
for entry in "${INTEGRITY_WHITELIST_FILES[@]}"; do
if [[ -n "$pattern" ]]; then
pattern+=$'\n'
fi
pattern+="$entry"
done
echo "$input" | grep -v -F "$pattern" || true
}
list_packages_rpm() {
rpm -qa 'imunify*' 2>/dev/null | sort
}
list_packages_dpkg() {
dpkg-query -W -f '${Status} ${Package}\n' 'imunify*' 2>/dev/null \
| sed -n 's/.* installed \(.*\)/\1/p' \
| sort
}
verify_package_rpm() {
local pkg="$1"
local raw_output=""
raw_output=$(rpm -V "$pkg" 2>&1) || raw_output="${raw_output}"
local filtered=""
filtered=$(echo "$raw_output" | filter_verify_noise | filter_whitelisted_files)
if [[ -n "$filtered" ]]; then
echo "$filtered"
return 1
fi
return 0
}
verify_package_dpkg() {
local pkg="$1"
local raw_output=""
raw_output=$(dpkg -V "$pkg" 2>&1) || raw_output="${raw_output}"
local filtered=""
filtered=$(echo "$raw_output" | filter_verify_noise | filter_whitelisted_files)
if [[ -n "$filtered" ]]; then
echo "$filtered"
return 1
fi
return 0
}
check_packages() {
local pkg_manager
pkg_manager=$(detect_pkg_manager)
local verify_supported="yes"
local overall_status="ok"
local broken_output=""
local packages=""
case "$pkg_manager" in
rpm)
packages=$(list_packages_rpm)
;;
dpkg)
packages=$(list_packages_dpkg)
if ! check_dpkg_verify_supported; then
verify_supported="no"
fi
;;
*)
verify_supported="no"
;;
esac
if [[ "$verify_supported" == "yes" ]] && [[ -n "$packages" ]]; then
while IFS= read -r pkg; do
[[ -z "$pkg" ]] && continue
local output=""
local rc=0
case "$pkg_manager" in
rpm)
output=$(verify_package_rpm "$pkg") || rc=$?
;;
dpkg)
output=$(verify_package_dpkg "$pkg") || rc=$?
;;
esac
if [[ $rc -ne 0 ]]; then
overall_status="broken"
broken_output+=$'\n'"BROKEN: ${pkg}"$'\n'"${output}"$'\n'
fi
done <<< "$packages"
fi
echo "PKG_MANAGER: ${pkg_manager}"
echo "VERIFY_SUPPORTED: ${verify_supported}"
echo "STATUS: ${overall_status}"
if [[ -n "$broken_output" ]]; then
echo "$broken_output"
fi
}
main_entrypoint() {
local output
output=$(check_packages)
echo "$output"
if echo "$output" | grep -q "^STATUS: broken"; then
return 1
fi
return 0
}
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
set -euo pipefail
main_entrypoint
fi