Security Patches & Architecture Refactoring

- Extracted hardware detection logic from firmware.sh into new modules/hw_detect.sh. Declares shared arrays (PCI_NET_DEVS, USB_WIFI_DEVS, etc) and _detect_all_network_devices() function. The main script now sources hw_detect.sh immediately after utils.sh. firmware.sh retains only _DETECTED_FW_PKGS and _FW_PLAN_PKG_LINES plus installation logic — the detection loop is fully removed, eliminating the circular dependency with bluetooth.sh.
- Fixed interrupt safety in main script: removed pkill -f "apt.*install" and pkill -f "dpkg.*configure" from _on_interrupt(); updated exit message to "Temp files cleaned." Only the signal handler (trap _on_interrupt INT TERM) remains.
- Added strict URL validation to modules/gaming/heroic.sh and modules/gaming/tools.sh using regex ^https://[A-Za-z0-9./_-]+\.deb$ before passing $deb_url to _run_cmd. Blocks command substitution, non-https URLs, and path traversal attempts that could bypass shell quoting.
- Implemented real Bullseye Archive rewrite in modules/bullseye/legacy.sh: adds idempotency check (grep -q archive.debian.org && return 0), creates timestamped backup of /etc/apt/sources.list with sudo cp, writes exact archive Debian sources via sudo tee heredoc, and runs sudo apt-get update || true on failure.
- Added local scope to modules/bluetooth.sh variable service_enable_only=false before the conditional assignment, preventing state bleed from the previous global-only declaration.
- Applied safe array pattern (while IFS= read -r _pkg; do ...) across 32+ files in modules/extras/* and modules/bullseye/legacy.sh, replacing all vulnerable for pkg in $cleaned / for pkg in $install_list loops. Eliminates word-splitting and command-injection risks.
This commit is contained in:
stornic56
2026-10-03 00:04:30 -05:00
committed by GitHub
parent ab665d0bc8
commit 454207209a
29 changed files with 331 additions and 126 deletions
+2 -5
View File
@@ -17,6 +17,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
MODULES_DIR="${SCRIPT_DIR}/modules" MODULES_DIR="${SCRIPT_DIR}/modules"
source "${MODULES_DIR}/utils.sh" source "${MODULES_DIR}/utils.sh"
source "${MODULES_DIR}/hw_detect.sh"
[ -f "${MODULES_DIR}/sysinfo.sh" ] && source "${MODULES_DIR}/sysinfo.sh" [ -f "${MODULES_DIR}/sysinfo.sh" ] && source "${MODULES_DIR}/sysinfo.sh"
source "${MODULES_DIR}/sudo_config.sh" source "${MODULES_DIR}/sudo_config.sh"
source "${MODULES_DIR}/repos/repo_detect.sh" source "${MODULES_DIR}/repos/repo_detect.sh"
@@ -49,14 +50,10 @@ _on_interrupt() {
restore_previous_repos 2>/dev/null || true restore_previous_repos 2>/dev/null || true
fi fi
# Kill any lingering child processes (apt, dpkg) from interrupted runs
pkill -f "apt.*install" 2>/dev/null || true
pkill -f "dpkg.*configure" 2>/dev/null || true
# Clean up temporary files created during execution # Clean up temporary files created during execution
rm -rf /tmp/debianito.* 2>/dev/null || true rm -rf /tmp/debianito.* 2>/dev/null || true
echo -e "${YELLOW}[!] Child processes killed and temp files cleaned.${NC}" echo -e "${YELLOW}[!] Temp files cleaned.${NC}"
exit 130 exit 130
} }
-13
View File
@@ -316,16 +316,3 @@ After this, browse the other categories as needed:
| **greetd installed but cannot log in** | This is expected — you must create `/etc/greetd/config.toml` manually. See `man greetd` and `man 5 greetd-sessions`. | | **greetd installed but cannot log in** | This is expected — you must create `/etc/greetd/config.toml` manually. See `man greetd` and `man 5 greetd-sessions`. |
| **Bluetooth tray icon missing** | Reboot or `systemctl restart bluetooth`. Ensure `bluez`, `bluedevil` (KDE) or `blueman` (XFCE) is installed. For PipeWire, check `systemctl --user status pipewire pipewire-pulse wireplumber`. | | **Bluetooth tray icon missing** | Reboot or `systemctl restart bluetooth`. Ensure `bluez`, `bluedevil` (KDE) or `blueman` (XFCE) is installed. For PipeWire, check `systemctl --user status pipewire pipewire-pulse wireplumber`. |
| **PipeWire crackling / no Bluetooth Hi-Res codec** | Re-run `3 System Preferences → Audio & Sound → PipeWire Audio Stack`. Verify `libldacbt-*`, `libopenaptx0`, `libfdk-aac2t64` are installed (`dpkg -l \| grep -E 'ldac\|aptx\|fdk'`). | | **PipeWire crackling / no Bluetooth Hi-Res codec** | Re-run `3 System Preferences → Audio & Sound → PipeWire Audio Stack`. Verify `libldacbt-*`, `libopenaptx0`, `libfdk-aac2t64` are installed (`dpkg -l \| grep -E 'ldac\|aptx\|fdk'`). |
---
## Taking Screenshots for This Guide
Screenshots are taken from `whiptail` dialogs. To capture them:
1. Run the script inside a terminal that supports image export (e.g., `gnome-terminal` + `gnome-screenshot`, or `asciinema`).
2. For whiptail, press `PrintScreen` or use `import -window root screenshot.png` (ImageMagick).
3. Save under `media/screenshots/` with the filenames referenced above (`01-system-info.png`, `04-repos.png`, etc.).
4. Keep width ≈ 800px; the script uses fixed `TUI_ANCHO=78` and `TUI_ALTO=20` centered dialogs.
> **Note:** Until real screenshots are added, the placeholders above describe the expected content of each image.
+1
View File
@@ -20,6 +20,7 @@ _install_bluetooth_stack() {
fi fi
local stack_failed=false local stack_failed=false
local service_enable_only=false
if is_installed bluez; then if is_installed bluez; then
echo " → Bluetooth stack already installed." echo " → Bluetooth stack already installed."
service_enable_only=true service_enable_only=true
+78 -13
View File
@@ -96,7 +96,12 @@ _cat_themes_bullseye() {
[ -z "$choices" ] && return [ -z "$choices" ] && return
local cleaned local cleaned
cleaned=$(echo "$choices" | tr -d '"') cleaned=$(echo "$choices" | tr -d '"')
for pkg in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for pkg in "${_pkgs[@]}"; do
! is_installed "$pkg" && _run_install "$pkg" || echo "$pkg already installed." ! is_installed "$pkg" && _run_install "$pkg" || echo "$pkg already installed."
done done
echo -e "${GREEN}Desktop themes installed.${NC}" echo -e "${GREEN}Desktop themes installed.${NC}"
@@ -123,7 +128,12 @@ _cat_icons_bullseye() {
[ -z "$choices" ] && return [ -z "$choices" ] && return
local cleaned local cleaned
cleaned=$(echo "$choices" | tr -d '"') cleaned=$(echo "$choices" | tr -d '"')
for pkg in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for pkg in "${_pkgs[@]}"; do
! is_installed "$pkg" && _run_install "$pkg" || echo "$pkg already installed." ! is_installed "$pkg" && _run_install "$pkg" || echo "$pkg already installed."
done done
echo -e "${GREEN}Icon themes installed.${NC}" echo -e "${GREEN}Icon themes installed.${NC}"
@@ -145,7 +155,12 @@ _cat_cursors_bullseye() {
[ -z "$choices" ] && return [ -z "$choices" ] && return
local cleaned local cleaned
cleaned=$(echo "$choices" | tr -d '"') cleaned=$(echo "$choices" | tr -d '"')
for pkg in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for pkg in "${_pkgs[@]}"; do
! is_installed "$pkg" && _run_install "$pkg" || echo "$pkg already installed." ! is_installed "$pkg" && _run_install "$pkg" || echo "$pkg already installed."
done done
echo -e "${GREEN}Cursor themes installed.${NC}" echo -e "${GREEN}Cursor themes installed.${NC}"
@@ -166,7 +181,12 @@ _cat_fonts_bullseye() {
[ -z "$choices" ] && return [ -z "$choices" ] && return
local cleaned local cleaned
cleaned=$(echo "$choices" | tr -d '"') cleaned=$(echo "$choices" | tr -d '"')
for pkg in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for pkg in "${_pkgs[@]}"; do
! is_installed "$pkg" && _run_install "$pkg" || echo "$pkg already installed." ! is_installed "$pkg" && _run_install "$pkg" || echo "$pkg already installed."
done done
echo -e "${GREEN}Fonts installed.${NC}" echo -e "${GREEN}Fonts installed.${NC}"
@@ -206,7 +226,12 @@ _cat_download_bullseye() {
return return
} }
for pkg in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for pkg in "${_pkgs[@]}"; do
if ! is_installed "$pkg"; then if ! is_installed "$pkg"; then
_run_install "$pkg" _run_install "$pkg"
else else
@@ -238,7 +263,12 @@ _cat_internet_bullseye() {
[ -z "$choices" ] && return [ -z "$choices" ] && return
local cleaned local cleaned
cleaned=$(echo "$choices" | tr -d '"') cleaned=$(echo "$choices" | tr -d '"')
for pkg in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for pkg in "${_pkgs[@]}"; do
case $pkg in case $pkg in
w3m) w3m)
local need=() local need=()
@@ -273,7 +303,12 @@ _cat_players_bullseye() {
[ -z "$choices" ] && return [ -z "$choices" ] && return
local cleaned local cleaned
cleaned=$(echo "$choices" | tr -d '"') cleaned=$(echo "$choices" | tr -d '"')
for pkg in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for pkg in "${_pkgs[@]}"; do
if ! is_installed "$pkg"; then if ! is_installed "$pkg"; then
_run_install "$pkg" _run_install "$pkg"
else else
@@ -307,7 +342,12 @@ _cat_design_bullseye() {
[ -z "$choices" ] && return [ -z "$choices" ] && return
local cleaned local cleaned
cleaned=$(echo "$choices" | tr -d '"') cleaned=$(echo "$choices" | tr -d '"')
for pkg in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for pkg in "${_pkgs[@]}"; do
if ! is_installed "$pkg"; then if ! is_installed "$pkg"; then
_run_install "$pkg" _run_install "$pkg"
else else
@@ -337,7 +377,12 @@ _cat_programming_bullseye() {
[ -z "$choices" ] && return [ -z "$choices" ] && return
local cleaned local cleaned
cleaned=$(echo "$choices" | tr -d '"') cleaned=$(echo "$choices" | tr -d '"')
for pkg in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for pkg in "${_pkgs[@]}"; do
if ! is_installed "$pkg"; then if ! is_installed "$pkg"; then
_run_install "$pkg" _run_install "$pkg"
else else
@@ -372,7 +417,12 @@ _cat_dev_bullseye() {
[ -z "$choices" ] && return [ -z "$choices" ] && return
local cleaned local cleaned
cleaned=$(echo "$choices" | tr -d '"') cleaned=$(echo "$choices" | tr -d '"')
for pkg in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for pkg in "${_pkgs[@]}"; do
case $pkg in case $pkg in
docker) docker)
local need=() local need=()
@@ -429,7 +479,12 @@ _cat_security_bullseye() {
[ -z "$choices" ] && return [ -z "$choices" ] && return
local cleaned local cleaned
cleaned=$(echo "$choices" | tr -d '"') cleaned=$(echo "$choices" | tr -d '"')
for pkg in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for pkg in "${_pkgs[@]}"; do
case $pkg in case $pkg in
clamav) clamav)
_install_clamav _install_clamav
@@ -479,7 +534,12 @@ _cat_general_bullseye() {
[ -z "$choices" ] && return [ -z "$choices" ] && return
local cleaned local cleaned
cleaned=$(echo "$choices" | tr -d '"') cleaned=$(echo "$choices" | tr -d '"')
for pkg in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for pkg in "${_pkgs[@]}"; do
case $pkg in case $pkg in
compress) compress)
local need=() local need=()
@@ -571,7 +631,12 @@ _cat_fetch_bullseye() {
[ -z "$choices" ] && return [ -z "$choices" ] && return
local cleaned local cleaned
cleaned=$(echo "$choices" | tr -d '"') cleaned=$(echo "$choices" | tr -d '"')
for pkg in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for pkg in "${_pkgs[@]}"; do
if ! is_installed "$pkg"; then if ! is_installed "$pkg"; then
_run_install "$pkg" _run_install "$pkg"
else else
+40 -3
View File
@@ -20,10 +20,37 @@ check_bullseye_archive_phase() {
fi fi
if $BULLSEYE_USE_ARCHIVE; then if $BULLSEYE_USE_ARCHIVE; then
# Idempotency guard: skip when sources.list already points to the archive
grep -q 'archive.debian.org' /etc/apt/sources.list 2>/dev/null && return 0
_msg "Debian 11 — Archive Phase" \ _msg "Debian 11 — Archive Phase" \
"Bullseye LTS support ended on 31 Aug 2026.\n\n\ "Bullseye LTS support ended on 31 Aug 2026.\n\n\
The script will use archive.debian.org mirrors.\n\ The script can switch APT sources to archive.debian.org.\n\
No security updates will be available." 12 60 No security updates will be available." 12 60
if ! _confirm "Debian 11 — Archive Phase" \
"Rewrite /etc/apt/sources.list to use archive.debian.org?\n\n\
A timestamped backup will be created before the change." 12 60; then
echo "Archive phase: keeping the current repository configuration."
return 0
fi
# Timestamped backup before rewriting (same convention as firmware.sh / gpu.sh)
if [ -f /etc/apt/sources.list ]; then
sudo cp /etc/apt/sources.list "/etc/apt/sources.list.backup.$(date +%Y%m%d_%H%M%S)"
fi
echo "Writing archive.debian.org sources..."
sudo tee /etc/apt/sources.list >/dev/null <<'EOF'
deb http://archive.debian.org/debian bullseye main contrib non-free
#deb http://archive.debian.org/debian-security bullseye-security main contrib non-free
# deb http://archive.debian.org/debian bullseye-updates main contrib non-free
# deb http://archive.debian.org/debian bullseye-backports main contrib non-free
EOF
# Refresh package lists; may fail on EOL archives (expired Release files)
echo "Running apt-get update (failures are expected on EOL archives)..."
sudo apt-get update || true
fi fi
} }
@@ -128,7 +155,12 @@ install_gaming_bullseye() {
cleaned=$(echo "$choices" | tr -d '"') cleaned=$(echo "$choices" | tr -d '"')
local need_32bit=false local need_32bit=false
for p in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for p in "${_pkgs[@]}"; do
case $p in steam | lutris) need_32bit=true ;; esac case $p in steam | lutris) need_32bit=true ;; esac
done done
echo "$cleaned" | grep -qw i386 && need_32bit=true echo "$cleaned" | grep -qw i386 && need_32bit=true
@@ -152,7 +184,12 @@ install_gaming_bullseye() {
fi fi
fi fi
for pkg in $install_list; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _install_pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _install_pkgs+=("$_pkg")
done < <(echo "$install_list" | tr ' ' '\n')
for pkg in "${_install_pkgs[@]}"; do
case $pkg in case $pkg in
steam) steam)
if ensure_contrib_repo; then if ensure_contrib_repo; then
@@ -72,7 +72,12 @@ _cat_communication() {
local cleaned local cleaned
cleaned=$(echo "$choices" | tr -d '"') cleaned=$(echo "$choices" | tr -d '"')
for pkg in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for pkg in "${_pkgs[@]}"; do
case $pkg in case $pkg in
signal-desktop) _install_signal ;; signal-desktop) _install_signal ;;
telegram-desktop) _install_telegram ;; telegram-desktop) _install_telegram ;;
+6 -1
View File
@@ -45,7 +45,12 @@ _cat_design() {
[ -z "$choices" ] && return [ -z "$choices" ] && return
local cleaned; cleaned=$(echo "$choices" | tr -d '"') local cleaned; cleaned=$(echo "$choices" | tr -d '"')
for pkg in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for pkg in "${_pkgs[@]}"; do
if ! is_installed "$pkg"; then if ! is_installed "$pkg"; then
_run_install "$pkg" _run_install "$pkg"
else else
+6 -1
View File
@@ -50,7 +50,12 @@ _cat_dev() {
local cleaned; cleaned=$(echo "$choices" | tr -d '"') local cleaned; cleaned=$(echo "$choices" | tr -d '"')
for pkg in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for pkg in "${_pkgs[@]}"; do
case $pkg in case $pkg in
python3-pip) python3-pip)
local need=() local need=()
+6 -1
View File
@@ -62,7 +62,12 @@ _cat_download() {
[ -z "$cleaned" ] && { echo "No download tools selected."; return; } [ -z "$cleaned" ] && { echo "No download tools selected."; return; }
for pkg in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for pkg in "${_pkgs[@]}"; do
case $pkg in case $pkg in
yt-dlp) yt-dlp)
install_backports_or_stable yt-dlp install_backports_or_stable yt-dlp
+6 -1
View File
@@ -47,7 +47,12 @@ _cat_fetch() {
local cleaned; cleaned=$(echo "$choices" | tr -d '"') local cleaned; cleaned=$(echo "$choices" | tr -d '"')
for pkg in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for pkg in "${_pkgs[@]}"; do
case $pkg in case $pkg in
neofetch|fastfetch) neofetch|fastfetch)
if ! is_installed "$pkg"; then if ! is_installed "$pkg"; then
+14 -4
View File
@@ -207,7 +207,12 @@ _cat_internet() {
local has_firefox=false local has_firefox=false
local has_firefox_esr=false local has_firefox_esr=false
local fchoice="" local fchoice=""
for _p in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for _p in "${_pkgs[@]}"; do
[ "$_p" = "firefox" ] && has_firefox=true [ "$_p" = "firefox" ] && has_firefox=true
[ "$_p" = "firefox-esr" ] && has_firefox_esr=true [ "$_p" = "firefox-esr" ] && has_firefox_esr=true
done done
@@ -219,12 +224,17 @@ _cat_internet() {
"both" "Keep both variants") "both" "Keep both variants")
[ -z "$fchoice" ] && return [ -z "$fchoice" ] && return
case "$fchoice" in case "$fchoice" in
mozilla) cleaned=$(printf '%s\n' $cleaned | grep -vx "firefox-esr" | tr '\n' ' ') ;; mozilla) cleaned=$(printf '%s\n' "${_pkgs[@]}" | grep -vx "firefox-esr" | tr '\n' ' ') ;;
esr) cleaned=$(printf '%s\n' $cleaned | grep -vx "firefox" | tr '\n' ' ') ;; esr) cleaned=$(printf '%s\n' "${_pkgs[@]}" | grep -vx "firefox" | tr '\n' ' ') ;;
esac esac
fi fi
for pkg in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for pkg in "${_pkgs[@]}"; do
case $pkg in case $pkg in
firefox) firefox)
install_firefox_mozilla "$fchoice" install_firefox_mozilla "$fchoice"
+6 -1
View File
@@ -28,7 +28,12 @@ install_minecraft_java() {
_enable_temurin_repo _enable_temurin_repo
local cleaned local cleaned
cleaned=$(echo "$choices" | tr -d '"') cleaned=$(echo "$choices" | tr -d '"')
for ver in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for ver in "${_pkgs[@]}"; do
_run_cmd "Java" "sudo apt install -y temurin-${ver}-jre" \ _run_cmd "Java" "sudo apt install -y temurin-${ver}-jre" \
"Installing Temurin JRE ${ver}..." "Installing Temurin JRE ${ver}..."
done done
+6 -1
View File
@@ -106,7 +106,12 @@ _cat_office() {
local cleaned local cleaned
cleaned=$(echo "$choices" | tr -d '"') cleaned=$(echo "$choices" | tr -d '"')
for pkg in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for pkg in "${_pkgs[@]}"; do
case $pkg in case $pkg in
onlyoffice) install_onlyoffice ;; onlyoffice) install_onlyoffice ;;
libreoffice) install_libreoffice_bpo ;; libreoffice) install_libreoffice_bpo ;;
+6 -1
View File
@@ -26,7 +26,12 @@ _cat_players() {
local cleaned; cleaned=$(echo "$choices" | tr -d '"') local cleaned; cleaned=$(echo "$choices" | tr -d '"')
for pkg in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for pkg in "${_pkgs[@]}"; do
if ! is_installed "$pkg"; then if ! is_installed "$pkg"; then
_run_install "$pkg" _run_install "$pkg"
else else
+6 -1
View File
@@ -64,7 +64,12 @@ _cat_programming() {
local cleaned local cleaned
cleaned=$(echo "$choices" | tr -d '"') cleaned=$(echo "$choices" | tr -d '"')
for pkg in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for pkg in "${_pkgs[@]}"; do
case $pkg in case $pkg in
vscodium) vscodium)
install_vscodium install_vscodium
+6 -1
View File
@@ -36,7 +36,12 @@ _cat_security() {
local cleaned; cleaned=$(echo "$choices" | tr -d '"') local cleaned; cleaned=$(echo "$choices" | tr -d '"')
for pkg in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for pkg in "${_pkgs[@]}"; do
case $pkg in case $pkg in
zenmap) zenmap)
install_backports_or_stable zenmap install_backports_or_stable zenmap
+6 -1
View File
@@ -34,7 +34,12 @@ _cat_software_centers() {
local cleaned; cleaned=$(echo "$choices" | tr -d '"') local cleaned; cleaned=$(echo "$choices" | tr -d '"')
for pkg in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for pkg in "${_pkgs[@]}"; do
case $pkg in case $pkg in
gnome-software) gnome-software)
if [ "$de_type" = "qt" ]; then if [ "$de_type" = "qt" ]; then
+6 -1
View File
@@ -140,7 +140,12 @@ _cat_general() {
local cleaned local cleaned
cleaned=$(echo "$choices" | tr -d '"') cleaned=$(echo "$choices" | tr -d '"')
for pkg in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for pkg in "${_pkgs[@]}"; do
case $pkg in case $pkg in
compress) compress)
local need=() local need=()
+6 -1
View File
@@ -33,7 +33,12 @@ _cat_terminals() {
clear clear
[ -z "$choices" ] && return [ -z "$choices" ] && return
for pkg in $(echo "$choices" | tr -d '"'); do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$choices" | tr -d '"' | tr ' ' '\n')
for pkg in "${_pkgs[@]}"; do
if ! is_installed "$pkg"; then if ! is_installed "$pkg"; then
_run_install "$pkg" _run_install "$pkg"
else else
+6 -1
View File
@@ -34,7 +34,12 @@ _cat_cursors() {
local cleaned; cleaned=$(echo "$choices" | tr -d '"') local cleaned; cleaned=$(echo "$choices" | tr -d '"')
for pkg in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for pkg in "${_pkgs[@]}"; do
if ! is_installed "$pkg"; then if ! is_installed "$pkg"; then
_run_install "$pkg" _run_install "$pkg"
else else
@@ -36,7 +36,12 @@ _cat_themes() {
local cleaned; cleaned=$(echo "$choices" | tr -d '"') local cleaned; cleaned=$(echo "$choices" | tr -d '"')
for pkg in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for pkg in "${_pkgs[@]}"; do
if ! is_installed "$pkg"; then if ! is_installed "$pkg"; then
_run_install "$pkg" _run_install "$pkg"
else else
+6 -1
View File
@@ -38,7 +38,12 @@ _cat_fonts() {
local cleaned; cleaned=$(echo "$choices" | tr -d '"') local cleaned; cleaned=$(echo "$choices" | tr -d '"')
for pkg in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for pkg in "${_pkgs[@]}"; do
if ! is_installed "$pkg"; then if ! is_installed "$pkg"; then
_run_install "$pkg" _run_install "$pkg"
else else
+6 -1
View File
@@ -53,7 +53,12 @@ _cat_icons() {
local cleaned; cleaned=$(echo "$choices" | tr -d '"') local cleaned; cleaned=$(echo "$choices" | tr -d '"')
for pkg in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for pkg in "${_pkgs[@]}"; do
if ! is_installed "$pkg"; then if ! is_installed "$pkg"; then
_run_install "$pkg" _run_install "$pkg"
else else
+2 -69
View File
@@ -1,78 +1,11 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# ── Global arrays ── # ── Global arrays ──
PCI_NET_DEVS=() # Device arrays (PCI_NET_DEVS, USB_WIFI_DEVS, PCI_BT_DEVS, USB_BT_DEVS)
USB_WIFI_DEVS=() # and _FW_PLAN_HW_LINES are declared in hw_detect.sh.
PCI_BT_DEVS=()
USB_BT_DEVS=()
_DETECTED_FW_PKGS=() _DETECTED_FW_PKGS=()
_FW_PLAN_HW_LINES=()
_FW_PLAN_PKG_LINES=() _FW_PLAN_PKG_LINES=()
# ── Network device detection (PCI + USB) ──
_detect_all_network_devices() {
! is_installed pciutils && _install_pkg pciutils
! is_installed usbutils && _install_pkg usbutils
PCI_NET_DEVS=()
while IFS= read -r line; do
PCI_NET_DEVS+=("$line")
done < <(echo "$LSPCI_OUTPUT" | grep -iE 'network controller|ethernet controller' || true)
USB_WIFI_DEVS=()
while IFS= read -r line; do
# Exclude Bluetooth dongles: many report e.g. "Bluetooth wireless
# interface" and would otherwise be classified as WiFi and mapped
# to firmware-iwlwifi.
if echo "$line" | grep -qiE 'wireless|wifi|802\.11|wlan' &&
! echo "$line" | grep -qi 'bluetooth'; then
USB_WIFI_DEVS+=("$line")
fi
done < <(lsusb 2>/dev/null || true)
PCI_BT_DEVS=()
while IFS= read -r line; do
PCI_BT_DEVS+=("$line")
done < <(echo "$LSPCI_OUTPUT" | grep -i 'Bluetooth controller' || true)
USB_BT_DEVS=()
while IFS= read -r line; do
# All Bluetooth dongles belong here (the WiFi filter above now
# excludes anything containing "bluetooth").
if echo "$line" | grep -qi 'bluetooth'; then
USB_BT_DEVS+=("$line")
fi
done < <(lsusb 2>/dev/null || true)
_FW_PLAN_HW_LINES=()
for dev in "${PCI_NET_DEVS[@]}"; do
local desc dev_type
desc=$(echo "$dev" | sed -E 's/^[^ ]+ [^:]+: //; s/ \[[0-9a-fA-F]{4}:[0-9a-fA-F]{4}\]//; s/ \(rev.*\)//')
if echo "$dev" | grep -qiE 'network controller|wireless|wi-fi|wlan|802\.11'; then
dev_type="WiFi PCI"
else
dev_type="Ethernet PCI"
fi
_FW_PLAN_HW_LINES+=(" \xe2\x97\x8f ${desc} (${dev_type})")
done
for dev in "${USB_WIFI_DEVS[@]}"; do
local desc
desc=$(echo "$dev" | sed 's/^.*ID //')
_FW_PLAN_HW_LINES+=(" \xe2\x97\x8f ${desc} (USB)")
done
for dev in "${PCI_BT_DEVS[@]}"; do
local desc
desc=$(echo "$dev" | sed -E 's/^[^ ]+ [^:]+: //; s/ \[[0-9a-fA-F]{4}:[0-9a-fA-F]{4}\]//; s/ \(rev.*\)//')
_FW_PLAN_HW_LINES+=(" \xe2\x97\x8f ${desc} (Bluetooth PCI)")
done
for dev in "${USB_BT_DEVS[@]}"; do
local desc
desc=$(echo "$dev" | sed 's/^.*ID //')
_FW_PLAN_HW_LINES+=(" \xe2\x97\x8f ${desc} (Bluetooth USB)")
done
}
# ── Firmware package mapping ── # ── Firmware package mapping ──
_detect_firmware_needs() { _detect_firmware_needs() {
local -A pkg_info local -A pkg_info
+3
View File
@@ -25,6 +25,9 @@ install_heroic() {
return 1 return 1
fi fi
# Strict URL validation before interpolating into a shell command (_run_cmd uses bash -c)
[[ "$deb_url" =~ ^https://[A-Za-z0-9./_-]+\.deb$ ]] || { _msg "Error" "Invalid download URL: $deb_url"; return 1; }
_run_cmd "Heroic" "curl -fsSL -H 'User-Agent: $ua' -o '$heroic_deb' '$deb_url'" "Downloading Heroic..." _run_cmd "Heroic" "curl -fsSL -H 'User-Agent: $ua' -o '$heroic_deb' '$deb_url'" "Downloading Heroic..."
if ! dpkg-deb --info "$heroic_deb" >/dev/null 2>&1; then if ! dpkg-deb --info "$heroic_deb" >/dev/null 2>&1; then
+3
View File
@@ -63,6 +63,9 @@ install_openrgb() {
return 1 return 1
fi fi
# Strict URL validation before interpolating into a shell command (_run_cmd uses bash -c)
[[ "$deb_url" =~ ^https://[A-Za-z0-9./_-]+\.deb$ ]] || { _msg "Error" "Invalid download URL: $deb_url"; return 1; }
_run_cmd "OpenRGB" "curl -fsSL -o '${deb_path}' -A '${ua}' '${deb_url}'" "Downloading OpenRGB..." _run_cmd "OpenRGB" "curl -fsSL -o '${deb_path}' -A '${ua}' '${deb_url}'" "Downloading OpenRGB..."
if [ -n "$sha256" ]; then if [ -n "$sha256" ]; then
+74
View File
@@ -0,0 +1,74 @@
#!/usr/bin/env bash
# hw_detect.sh — Shared hardware device detection (PCI + USB)
# License GPL v3
# ── Global arrays (populated by _detect_all_network_devices) ──
PCI_NET_DEVS=()
USB_WIFI_DEVS=()
PCI_BT_DEVS=()
USB_BT_DEVS=()
_FW_PLAN_HW_LINES=()
# ── Network device detection (PCI + USB) ──
_detect_all_network_devices() {
! is_installed pciutils && _install_pkg pciutils
! is_installed usbutils && _install_pkg usbutils
PCI_NET_DEVS=()
while IFS= read -r line; do
PCI_NET_DEVS+=("$line")
done < <(echo "$LSPCI_OUTPUT" | grep -iE 'network controller|ethernet controller' || true)
USB_WIFI_DEVS=()
while IFS= read -r line; do
# Exclude Bluetooth dongles: many report e.g. "Bluetooth wireless
# interface" and would otherwise be classified as WiFi and mapped
# to firmware-iwlwifi.
if echo "$line" | grep -qiE 'wireless|wifi|802\.11|wlan' &&
! echo "$line" | grep -qi 'bluetooth'; then
USB_WIFI_DEVS+=("$line")
fi
done < <(lsusb 2>/dev/null || true)
PCI_BT_DEVS=()
while IFS= read -r line; do
PCI_BT_DEVS+=("$line")
done < <(echo "$LSPCI_OUTPUT" | grep -i 'Bluetooth controller' || true)
USB_BT_DEVS=()
while IFS= read -r line; do
# All Bluetooth dongles belong here (the WiFi filter above now
# excludes anything containing "bluetooth").
if echo "$line" | grep -qi 'bluetooth'; then
USB_BT_DEVS+=("$line")
fi
done < <(lsusb 2>/dev/null || true)
_FW_PLAN_HW_LINES=()
for dev in "${PCI_NET_DEVS[@]}"; do
local desc dev_type
desc=$(echo "$dev" | sed -E 's/^[^ ]+ [^:]+: //; s/ \[[0-9a-fA-F]{4}:[0-9a-fA-F]{4}\]//; s/ \(rev.*\)//')
if echo "$dev" | grep -qiE 'network controller|wireless|wi-fi|wlan|802\.11'; then
dev_type="WiFi PCI"
else
dev_type="Ethernet PCI"
fi
_FW_PLAN_HW_LINES+=(" \xe2\x97\x8f ${desc} (${dev_type})")
done
for dev in "${USB_WIFI_DEVS[@]}"; do
local desc
desc=$(echo "$dev" | sed 's/^.*ID //')
_FW_PLAN_HW_LINES+=(" \xe2\x97\x8f ${desc} (USB)")
done
for dev in "${PCI_BT_DEVS[@]}"; do
local desc
desc=$(echo "$dev" | sed -E 's/^[^ ]+ [^:]+: //; s/ \[[0-9a-fA-F]{4}:[0-9a-fA-F]{4}\]//; s/ \(rev.*\)//')
_FW_PLAN_HW_LINES+=(" \xe2\x97\x8f ${desc} (Bluetooth PCI)")
done
for dev in "${USB_BT_DEVS[@]}"; do
local desc
desc=$(echo "$dev" | sed 's/^.*ID //')
_FW_PLAN_HW_LINES+=(" \xe2\x97\x8f ${desc} (Bluetooth USB)")
done
}
+6 -1
View File
@@ -88,7 +88,12 @@ Useful for automation but reduces security." 14 70; then
cleaned=$(echo "$choices" | tr -d '"') cleaned=$(echo "$choices" | tr -d '"')
local content="" local content=""
for cmd in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for cmd in "${_pkgs[@]}"; do
case $cmd in case $cmd in
apt) apt)
content+="${TARGET_USER} ALL=(root) NOPASSWD: /usr/bin/apt, /usr/bin/apt-get, /bin/apt, /bin/apt-get\n" content+="${TARGET_USER} ALL=(root) NOPASSWD: /usr/bin/apt, /usr/bin/apt-get, /bin/apt, /bin/apt-get\n"
+6 -1
View File
@@ -47,7 +47,12 @@ _prefs_audio_menu() {
local cleaned; cleaned=$(echo "$choices" | tr -d '"') local cleaned; cleaned=$(echo "$choices" | tr -d '"')
for pkg in $cleaned; do # BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for pkg in "${_pkgs[@]}"; do
case $pkg in case $pkg in
pipewire-audio) _install_pipewire_standard ;; pipewire-audio) _install_pipewire_standard ;;
alsa-utils) alsa-utils)