Add greylist support for challenge-required packages

- Create pacman_greylist.txt with virtualbox as initial entry
- Add is_greylisted_package_name() for substring matching
- Add remove_installed_greylisted_packages() to auto-uninstall
- Replace hardcoded VirtualBox check with generic greylist check
- Update installer to copy greylist file
This commit is contained in:
Krzysztof kuhy Rudnicki 2025-12-04 21:33:29 +01:00
parent c95462c5db
commit 95da70203b
3 changed files with 104 additions and 40 deletions

View File

@ -3,9 +3,9 @@
# Auto-sudo functionality
if [ "$EUID" -ne 0 ]; then
echo "Executing with sudo..."
sudo "$0" "$@"
exit $?
echo "Executing with sudo..."
sudo "$0" "$@"
exit $?
fi
# Colors
@ -21,21 +21,23 @@ WRAPPER_SOURCE="$(dirname "$0")/pacman_wrapper.sh"
WORDS_SOURCE="$(dirname "$0")/words.txt"
BLOCKED_SOURCE="$(dirname "$0")/pacman_blocked_keywords.txt"
WHITELIST_SOURCE="$(dirname "$0")/pacman_whitelist.txt"
GREYLIST_SOURCE="$(dirname "$0")/pacman_greylist.txt"
INSTALL_DIR="/usr/local/bin"
WRAPPER_DEST="${INSTALL_DIR}/pacman_wrapper"
WORDS_DEST="${INSTALL_DIR}/words.txt"
BLOCKED_DEST="${INSTALL_DIR}/pacman_blocked_keywords.txt"
WHITELIST_DEST="${INSTALL_DIR}/pacman_whitelist.txt"
GREYLIST_DEST="${INSTALL_DIR}/pacman_greylist.txt"
# Check if script is run as root
if [ "$EUID" -ne 0 ]; then
echo -e "${RED}Please run as root${NC}"
exit 1
echo -e "${RED}Please run as root${NC}"
exit 1
fi
# Check if the wrapper script exists
if [ ! -f "$WRAPPER_SOURCE" ]; then
echo -e "${RED}Error: Wrapper script not found at ${WRAPPER_SOURCE}${NC}"
exit 1
echo -e "${RED}Error: Wrapper script not found at ${WRAPPER_SOURCE}${NC}"
exit 1
fi
echo -e "${CYAN}Installing pacman wrapper...${NC}"
@ -45,26 +47,32 @@ echo -e "${BLUE}Copying wrapper script to ${WRAPPER_DEST}...${NC}"
cp "$WRAPPER_SOURCE" "$WRAPPER_DEST"
cp "$WORDS_SOURCE" "$WORDS_DEST"
if [ -f "$BLOCKED_SOURCE" ]; then
cp "$BLOCKED_SOURCE" "$BLOCKED_DEST"
cp "$BLOCKED_SOURCE" "$BLOCKED_DEST"
else
echo -e "${YELLOW}Warning:${NC} Missing blocked keywords source at ${BLOCKED_SOURCE}${NC}"
echo -e "${YELLOW}Warning:${NC} Missing blocked keywords source at ${BLOCKED_SOURCE}${NC}"
fi
if [ -f "$WHITELIST_SOURCE" ]; then
cp "$WHITELIST_SOURCE" "$WHITELIST_DEST"
cp "$WHITELIST_SOURCE" "$WHITELIST_DEST"
else
echo -e "${YELLOW}Warning:${NC} Missing whitelist source at ${WHITELIST_SOURCE}${NC}"
echo -e "${YELLOW}Warning:${NC} Missing whitelist source at ${WHITELIST_SOURCE}${NC}"
fi
if [ -f "$GREYLIST_SOURCE" ]; then
cp "$GREYLIST_SOURCE" "$GREYLIST_DEST"
else
echo -e "${YELLOW}Warning:${NC} Missing greylist source at ${GREYLIST_SOURCE}${NC}"
fi
chmod +x "$WRAPPER_DEST"
chmod 644 "$WORDS_DEST" "$BLOCKED_DEST" "$WHITELIST_DEST" 2> /dev/null || true
chmod 644 "$WORDS_DEST" "$BLOCKED_DEST" "$WHITELIST_DEST" "$GREYLIST_DEST" 2>/dev/null || true
# Automatically use symbolic link installation method
echo -e "${YELLOW}Installing using symbolic link method...${NC}"
# Backup original pacman
if [ ! -f "/usr/bin/pacman.orig" ]; then
echo -e "${BLUE}Backing up original pacman to /usr/bin/pacman.orig...${NC}"
cp /usr/bin/pacman /usr/bin/pacman.orig
echo -e "${BLUE}Backing up original pacman to /usr/bin/pacman.orig...${NC}"
cp /usr/bin/pacman /usr/bin/pacman.orig
fi
# Update the PACMAN_BIN variable in the wrapper to point to the original

View File

@ -0,0 +1,4 @@
# Packages matching any of these substrings require a challenge to install.
# They will also be uninstalled if found already installed.
# Lines starting with # are comments.
virtualbox

View File

@ -15,6 +15,7 @@ PACMAN_BIN="/usr/bin/pacman"
declare -a BLOCKED_KEYWORDS_LIST=()
declare -a WHITELISTED_NAMES_LIST=()
declare -a GREYLISTED_KEYWORDS_LIST=()
POLICY_LISTS_LOADED=0
load_policy_lists() {
@ -26,6 +27,7 @@ load_policy_lists() {
script_dir="$(dirname "$(readlink -f "$0")")"
local blocked_file="$script_dir/pacman_blocked_keywords.txt"
local whitelist_file="$script_dir/pacman_whitelist.txt"
local greylist_file="$script_dir/pacman_greylist.txt"
if [[ -f $blocked_file ]]; then
mapfile -t BLOCKED_KEYWORDS_LIST < <(sed 's/\r$//' "$blocked_file" | grep -Ev '^[[:space:]]*(#|$)' || true)
@ -40,6 +42,12 @@ load_policy_lists() {
WHITELISTED_NAMES_LIST=()
fi
if [[ -f $greylist_file ]]; then
mapfile -t GREYLISTED_KEYWORDS_LIST < <(sed 's/\r$//' "$greylist_file" | grep -Ev '^[[:space:]]*(#|$)' || true)
else
GREYLISTED_KEYWORDS_LIST=()
fi
for i in "${!BLOCKED_KEYWORDS_LIST[@]}"; do
BLOCKED_KEYWORDS_LIST[i]="${BLOCKED_KEYWORDS_LIST[i],,}"
done
@ -48,6 +56,10 @@ load_policy_lists() {
WHITELISTED_NAMES_LIST[i]="${WHITELISTED_NAMES_LIST[i],,}"
done
for i in "${!GREYLISTED_KEYWORDS_LIST[@]}"; do
GREYLISTED_KEYWORDS_LIST[i]="${GREYLISTED_KEYWORDS_LIST[i],,}"
done
POLICY_LISTS_LOADED=1
}
# Determine if this invocation may perform a transaction (upgrade/install/remove)
@ -186,7 +198,7 @@ function is_blocked_package_name() {
done
for keyword in "${BLOCKED_KEYWORDS_LIST[@]}"; do
if [[ -n $keyword && $normalized == *"$keyword"* ]]; then
if [[ $normalized == *"$keyword"* ]]; then
return 0
fi
done
@ -194,6 +206,22 @@ function is_blocked_package_name() {
return 1
}
# Helper: return 0 if the given package name is greylisted (challenge required)
function is_greylisted_package_name() {
load_policy_lists
local normalized="${1,,}"
for keyword in "${GREYLISTED_KEYWORDS_LIST[@]}"; do
if [[ $normalized == *"$keyword"* ]]; then
return 0
fi
done
return 1
}
# Helper: detect if current invocation includes --noconfirm
# Helper: detect if current invocation includes --noconfirm
function has_noconfirm_flag() {
for arg in "$@"; do
@ -341,6 +369,33 @@ function remove_installed_blocked_packages() {
return $rc
}
# Cleanup: remove any installed greylisted packages (challenge-required packages)
function remove_installed_greylisted_packages() {
# List installed package names
mapfile -t installed_names < <("$PACMAN_BIN" -Qq 2>/dev/null)
local to_remove=()
for name in "${installed_names[@]}"; do
if is_greylisted_package_name "$name"; then
to_remove+=("$name")
fi
done
if [[ ${#to_remove[@]} -eq 0 ]]; then
return 0
fi
echo -e "${YELLOW}Greylist cleanup:${NC} Removing greylisted installed packages: ${BOLD}${to_remove[*]}${NC}" >&2
local remove_cmd=("$PACMAN_BIN" -Rns --noconfirm)
"${remove_cmd[@]}" "${to_remove[@]}"
local rc=$?
if [[ $rc -ne 0 ]]; then
echo -e "${RED}Greylist cleanup removal failed with exit code ${rc}.${NC}" >&2
else
echo -e "${GREEN}Greylist cleanup removal completed for: ${to_remove[*]}${NC}" >&2
fi
return $rc
}
# Function to check if user is trying to install packages that are always blocked
function check_for_always_blocked() {
# Check if the command is an installation command
@ -536,10 +591,7 @@ function prompt_for_steam_challenge() {
fi
}
function check_for_virtualbox() {
# List of VirtualBox-related packages
local vbox_packages=("virtualbox" "virtualbox-host-modules-arch" "virtualbox-guest-iso" "virtualbox-ext-oracle")
function check_for_greylisted() {
# Check if the command is an installation command
if [[ $1 == "-S" || $1 == "-Sy" || $1 == "-Syu" || $1 == "-Syyu" || $1 == "-U" ]]; then
# Check all arguments
@ -547,22 +599,19 @@ function check_for_virtualbox() {
# Strip repository prefix if present
local package_name="${arg##*/}"
# Check if argument matches any VirtualBox package
for package in "${vbox_packages[@]}"; do
if [[ $arg == "$package" || $arg == *"/$package-"* || $arg == *"/$package/"* ||
$arg == *"/$package" || $package_name == "$package" ]]; then
return 0 # VirtualBox package found
fi
done
# Check if package name matches any greylisted keyword
if is_greylisted_package_name "$package_name"; then
return 0 # Greylisted package found
fi
done
fi
return 1 # No VirtualBox package found
return 1 # No greylisted package found
}
# Function to prompt for solving a word unscrambling challenge (for virtualbox - always active)
function prompt_for_virtualbox_challenge() {
echo -e "${YELLOW}WARNING: You are trying to install VirtualBox.${NC}"
echo -e "${YELLOW}VirtualBox challenge will begin shortly...${NC}"
# Function to prompt for solving a word unscrambling challenge (for greylisted packages - always active)
function prompt_for_greylist_challenge() {
echo -e "${YELLOW}WARNING: You are trying to install a greylisted package.${NC}"
echo -e "${YELLOW}Greylist challenge will begin shortly...${NC}"
# Sleep for random 10-30 seconds
sleep_duration=$((RANDOM % 20 + 10))
@ -578,9 +627,9 @@ function prompt_for_virtualbox_challenge() {
return 1
fi
# Choose a specific word length (6, 7, or 8 characters for VirtualBox)
# Choose a specific word length (6 characters for greylist challenge)
word_length=6
echo -e "${CYAN}VirtualBox challenge: Words with ${word_length} letters${NC}"
echo -e "${CYAN}Greylist challenge: Words with ${word_length} letters${NC}"
# Filter words by the specific chosen length and load random words
words_count=120
@ -625,7 +674,7 @@ function prompt_for_virtualbox_challenge() {
fi
echo -e "\n${YELLOW}One of those words has been scrambled to:${NC} ${CYAN}$scrambled_word${NC}"
echo -e "${YELLOW}Unscramble the word to proceed with VirtualBox installation (you have 90 seconds):${NC}"
echo -e "${YELLOW}Unscramble the word to proceed with installation (you have 90 seconds):${NC}"
# Set up a background process to display the timer
(
@ -657,7 +706,7 @@ function prompt_for_virtualbox_challenge() {
# Check if read timed out
if [[ $read_status -ne 0 ]]; then
echo -e "${RED}Time's up! VirtualBox challenge failed. The correct word was '$target_word'.${NC}"
echo -e "${RED}Time's up! Greylist challenge failed. The correct word was '$target_word'.${NC}"
return 1
fi
@ -665,7 +714,7 @@ function prompt_for_virtualbox_challenge() {
user_input=$(echo "$user_input" | tr '[:lower:]' '[:upper:]' | xargs)
if [[ $user_input == "$target_word" ]]; then
echo -e "${GREEN}Correct! Proceeding with VirtualBox installation...${NC}"
echo -e "${GREEN}Correct! Proceeding with installation...${NC}"
# Add sleep after successful challenge completion (15-35 seconds)
post_challenge_sleep=$((RANDOM % 20 + 15))
@ -673,7 +722,7 @@ function prompt_for_virtualbox_challenge() {
return 0
else
echo -e "${RED}Incorrect answer. VirtualBox installation aborted. The correct word was '$target_word'.${NC}"
echo -e "${RED}Incorrect answer. Installation aborted. The correct word was '$target_word'.${NC}"
return 1
fi
}
@ -703,9 +752,9 @@ if check_for_steam "$@"; then
fi
fi
# Check for VirtualBox (challenge-eligible package)
if check_for_virtualbox "$@"; then
if ! prompt_for_virtualbox_challenge; then
# Check for greylisted packages (challenge-eligible)
if check_for_greylisted "$@"; then
if ! prompt_for_greylist_challenge; then
exit 1
fi
fi
@ -750,6 +799,9 @@ fi
# After any operation, remove installed blocked packages as part of policy enforcement
remove_installed_blocked_packages "$@"
# Also remove installed greylisted packages
remove_installed_greylisted_packages "$@"
# Display some helpful tips depending on the operation
if [[ $1 == "-S" || $1 == "-S "* ]] && [ $exit_code -eq 0 ]; then
echo -e "${CYAN}Tip:${NC} You may need to log out or restart to use some newly installed software."