diff --git a/scripts/digital_wellbeing/pacman/install_pacman_wrapper.sh b/scripts/digital_wellbeing/pacman/install_pacman_wrapper.sh index 3eefad5..0a400fe 100755 --- a/scripts/digital_wellbeing/pacman/install_pacman_wrapper.sh +++ b/scripts/digital_wellbeing/pacman/install_pacman_wrapper.sh @@ -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 diff --git a/scripts/digital_wellbeing/pacman/pacman_greylist.txt b/scripts/digital_wellbeing/pacman/pacman_greylist.txt new file mode 100644 index 0000000..b9d41d8 --- /dev/null +++ b/scripts/digital_wellbeing/pacman/pacman_greylist.txt @@ -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 diff --git a/scripts/digital_wellbeing/pacman/pacman_wrapper.sh b/scripts/digital_wellbeing/pacman/pacman_wrapper.sh index 7c15f5e..b97edcf 100755 --- a/scripts/digital_wellbeing/pacman/pacman_wrapper.sh +++ b/scripts/digital_wellbeing/pacman/pacman_wrapper.sh @@ -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."