Author Topic: Roundcube vulnerability  (Read 3517 times)

0 Members and 2 Guests are viewing this topic.

Offline
*****
Re: Roundcube vulnerability
« Reply #15 on: February 11, 2026, 11:58:43 AM »
Just to affirm Starburst's previous guide to update Roundcube in light of the current vulnerability:
https://starburst.help/control-web-panel-cwp/control-web-panel-cwp-admin-tutorials/update-roundcube-webmail-to-version-1-5-11-in-cwp-on-almalinux-8-9/
or follow Sandeep's guide here:
https://www.alphagnu.com/topic/33-update-cwp-roundcube-mail-version-158-%E2%80%93-control-web-panel/
Simply update the Roundcube version number to 1.5.13 in the directions and download links and you will obtain a CWP-compatible LTS version of Roundcube, safe from the latest CVE.


Offline
***
Re: Roundcube vulnerability
« Reply #17 on: February 21, 2026, 10:40:13 PM »
Hi guys,

I've developed a script to update Roundcube for CWP to the latest LTS version.

What does the script do ?

1. I parses the page:
https://roundcube.net/download/
to identify the latest LTS version of Roundcub and URL to .tag.gz file.
2. Compares the versions (installed and available at the website)
3. If the installed version is older than available then:

3a. Makes backup of the currently installed Roundcube
3b. Downloads the .tar.gz file from the website
3c. Checks the checksum to make sure the downloaded file isn't corrupted
3d. Updates Roundcube
3e. Sends a notification to the user (address is specified in the script)

If the versions are the same or installed version is never then just sends an simple notification like "no update needed".
Code: [Select]
#!/usr/bin/env bash

####################################################################################
#                                                                                  #
#  The MIT License (MIT)                                                           #
#                                                                                  #
#  Copyright (c) 2026 BeinHost.com                                                 #
#                                                                                  #
#  Permission is hereby granted, free of charge, to any person obtaining a copy    #
#  of this software and associated documentation files (the "Software"), to deal   #
#  in the Software without restriction, including without limitation the rights    #
#  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell       #
#  copies of the Software, and to permit persons to whom the Software is           #
#  furnished to do so, subject to the following conditions:                        #
#                                                                                  #
#  The above copyright notice and this permission notice shall be included in all  #
#  copies or substantial portions of the Software.                                 #
#                                                                                  #
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR      #
#  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,        #
#  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE     #
#  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER          #
#  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,   #
#  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE   #
#  SOFTWARE.                                                                       #
#                                                                                  #
####################################################################################

########################################
# CONFIG
########################################

BASE_DIR="/usr/local/cwpsrv/var/services"
INSTALL_DIR="$BASE_DIR/roundcube"
PAGE_URL="https://roundcube.net/download/"
OWNER="cwpsvc:cwpsvc"
EMAIL="support@beinhost.com"
SUBJECT_PREFIX="[Roundcube Updater]"

cd "$BASE_DIR"

########################################
# Send notofication
########################################

send_email() {
    local subject="$1"
    local body="$2"

    # use mail command
    echo -e "$body" | mail -s "$SUBJECT_PREFIX $subject" "$EMAIL"
}

########################################
# Detect installed version
########################################

INI_FILE="$INSTALL_DIR/program/include/iniset.php"

if [[ ! -f "$INI_FILE" ]]; then
    echo "Cannot detect installed version (iniset.php missing)."
    exit 2
fi

installed_version=$(grep -oE "RCMAIL_VERSION',[[:space:]]*'[^']+'" \
    "$INI_FILE" | sed -E "s/.*'([^']+)'.*/\1/")
installed_version=$(echo "$installed_version" | tr -d '\r\n[:space:]')

echo "Installed version: $installed_version"

########################################
# Detect latest LTS version + checksum
########################################

lts_block=$(curl -fsSL "$PAGE_URL" \
  | awk '/<h2 id="lts">/,/<\/table>/')

download_url=$(echo "$lts_block" \
  | grep -oE 'https://[^"]+-complete\.tar\.gz' \
  | head -n1)

latest_version=$(echo "$download_url" \
  | sed -E 's/.*roundcubemail-([0-9]+\.[0-9]+\.[0-9]+)-complete\.tar\.gz/\1/')
latest_version=$(echo "$latest_version" | tr -d '\r\n[:space:]')

sha256_expected=$(echo "$lts_block" \
  | grep -oE '[a-f0-9]{64}' \
  | head -n1)

if [[ -z "$download_url" || -z "$latest_version" || -z "$sha256_expected" ]]; then
    echo "Failed to detect latest LTS release."
    send_email "Update failed" "Server: `hostname`\nFailed to detect latest LTS release.\nTime: $(date)"
    exit 3
fi

########################################
# Compare versions
########################################

version_gt() {
    [ "$1" = "$2" ] && return 1

    local IFS=.
    local i ver1=($1) ver2=($2)

    for ((i=${#ver1[@]}; i<${#ver2[@]}; i++)); do ver1[i]=0; done
    for ((i=${#ver2[@]}; i<${#ver1[@]}; i++)); do ver2[i]=0; done

    for ((i=0; i<${#ver1[@]}; i++)); do
        if ((10#${ver1[i]} > 10#${ver2[i]})); then return 0; fi
        if ((10#${ver1[i]} < 10#${ver2[i]})); then return 1; fi
    done

    return 1
}

#echo "Installed: [$installed_version]"
#echo "Latest:    [$latest_version]"

if version_gt "$latest_version" "$installed_version"; then
    echo "Update available."
    send_email "Update successful" "Server: `hostname`\nStatus: Roundcube updated successfully!\nPrevious version: $installed_version\nNew version: $latest_version\nBackup directory: $backup_dir\nTime: $(date)"

elif version_gt "$installed_version" "$latest_version"; then
    echo "Installed version is newer than LTS."
    send_email "Installed version newer than LTS" "Server: `hostname`\nStatus: Installed Roundcube version ($installed_version) is newer than latest LTS ($latest_version).\nNo update performed.\nTime: $(date)"
else
    echo "Latest LTS version:" "$latest_version"
    echo "Already up to date."
    send_email "No update needed" "Server: `hostname`\nStatus: Roundcube is already up to date.\nInstalled version: $installed_version\nLatest LTS: $latest_version\nTime: $(date)"
    exit 0
fi

########################################
# Backup current installation
########################################

backup_dir="roundcube_backup_v${installed_version}_$(date +%F_%H%M%S)"

echo "Creating backup: $backup_dir"
cp -a roundcube "$backup_dir"

########################################
# Download release
########################################

filename=$(basename "$download_url")

echo "Downloading $filename"
curl -fL -o "$filename" "$download_url"

########################################
# Verify SHA256
########################################

echo "Verifying checksum..."
sha256_actual=$(sha256sum "$filename" | awk '{print $1}')

if [[ "$sha256_actual" != "$sha256_expected" ]]; then
    echo "Checksum verification FAILED!"
    rm -f "$filename"
    send_email "Server: `hostname`\nUpdate FAILED" "Status: Roundcube update failed!\nInstalled version: $installed_version\nLatest version: $latest_version\nBackup: $backup_dir\nReason: SHA256 mismatch or extraction failure\nTime: $(date)"
    exit 4
fi

echo "Checksum OK."

########################################
# Extract + Install
########################################

echo "Extracting..."
tar -xzf "$filename"

src_dir="roundcubemail-$latest_version"

if [[ ! -d "$src_dir" ]]; then
    echo "Extraction failed. Directory $src_dir not found."
    exit 5
fi

echo "Running install script..."
yes | "$src_dir/bin/installto.sh" "$INSTALL_DIR"

########################################
# Fix permissions
########################################

chown -R "$OWNER" roundcube

########################################
# Cleanup
########################################

rm -rf "$src_dir" "$filename"

echo "Update completed successfully!"
echo "Now running version: $latest_version"

send_email "Update successful.\nStatus: Roundcube updated: $installed_version -> $latest_version\nBackup: $backup_dir"


I tested the script some time and it worked fine for me. However, please note, you use the script on your own risk (MIT License) )

Offline
*****
Re: Roundcube vulnerability
« Reply #18 on: February 22, 2026, 12:07:32 AM »
Thanks for that! I've added it into my daily cron routines.
One thing I do is add an authentication hook at the beginning of privileged scripts, in case it is run interactively:
Code: [Select]
# Authentication
sudo -p "Please authenticate (admin password): " printf "" || {
    echo "Abort: could not authenticate" >&2
    exit 1
}
And then close out the script with
Code: [Select]
sudo -kto remove cached credentials.

Offline
***
Re: Roundcube vulnerability
« Reply #19 on: February 22, 2026, 10:41:34 AM »
I just set the permissions on the script like:
Code: [Select]
chown admin:admin /script/update_roundcube.sh # replace "admin:admin" with your admin username and group.
chmod 700 /script/update_roundcube.sh
so no one except "admin" would be  allowed to run the script.

I should probably have written these instructions in my previous message.

Offline
***
Re: Roundcube vulnerability
« Reply #20 on: February 22, 2026, 03:10:42 PM »
Update to the script listed here:
http://forum.centos-webpanel.com/updates/roundcube-vulnerability/msg53064/#msg53064

Changelog:
Added the option to enable/disable email notifications
Code: [Select]
ENABLE_NOTIFICATIONS=true    # true/false to enable/disable notificationsThanks @overseer for the idea

Code: [Select]
#!/usr/bin/env bash

####################################################################################
#                                                                                  #
#  The MIT License (MIT)                                                           #
#                                                                                  #
#  Copyright (c) 2026 BeinHost.com                                                 #
#                                                                                  #
#  Permission is hereby granted, free of charge, to any person obtaining a copy    #
#  of this software and associated documentation files (the "Software"), to deal   #
#  in the Software without restriction, including without limitation the rights    #
#  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell       #
#  copies of the Software, and to permit persons to whom the Software is           #
#  furnished to do so, subject to the following conditions:                        #
#                                                                                  #
#  The above copyright notice and this permission notice shall be included in all  #
#  copies or substantial portions of the Software.                                 #
#                                                                                  #
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR      #
#  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,        #
#  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE     #
#  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER          #
#  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,   #
#  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE   #
#  SOFTWARE.                                                                       #
#                                                                                  #
####################################################################################

########################################
# CONFIG
########################################

BASE_DIR="/usr/local/cwpsrv/var/services"
INSTALL_DIR="$BASE_DIR/roundcube"
PAGE_URL="https://roundcube.net/download/"
OWNER="cwpsvc:cwpsvc"
EMAIL="address@domain.com" # set your email address here
ENABLE_NOTIFICATIONS=true    # true/false to enable/disable notifications
SUBJECT_PREFIX="[Roundcube Updater]"

cd "$BASE_DIR"

########################################
# Send notofication
########################################

send_email() {
    if [ "$ENABLE_NOTIFICATIONS" = true ]; then
        local subject="$1"
        local body="$2"

        # use mail command
        echo -e "$body" | mail -s "$SUBJECT_PREFIX $subject" "$EMAIL"
    fi
}

########################################
# Detect installed version
########################################

INI_FILE="$INSTALL_DIR/program/include/iniset.php"

if [[ ! -f "$INI_FILE" ]]; then
    echo "Cannot detect installed version (iniset.php missing)."
    exit 2
fi

installed_version=$(grep -oE "RCMAIL_VERSION',[[:space:]]*'[^']+'" \
    "$INI_FILE" | sed -E "s/.*'([^']+)'.*/\1/")
installed_version=$(echo "$installed_version" | tr -d '\r\n[:space:]')

echo "Installed version: $installed_version"

########################################
# Detect latest LTS version + checksum
########################################

lts_block=$(curl -fsSL "$PAGE_URL" \
  | awk '/<h2 id="lts">/,/<\/table>/')

download_url=$(echo "$lts_block" \
  | grep -oE 'https://[^"]+-complete\.tar\.gz' \
  | head -n1)

latest_version=$(echo "$download_url" \
  | sed -E 's/.*roundcubemail-([0-9]+\.[0-9]+\.[0-9]+)-complete\.tar\.gz/\1/')
latest_version=$(echo "$latest_version" | tr -d '\r\n[:space:]')

sha256_expected=$(echo "$lts_block" \
  | grep -oE '[a-f0-9]{64}' \
  | head -n1)

if [[ -z "$download_url" || -z "$latest_version" || -z "$sha256_expected" ]]; then
    echo "Failed to detect latest LTS release."
    send_email "Update failed" "Server: `hostname`\nFailed to detect latest LTS release.\nTime: $(date)"
    exit 3
fi

########################################
# Compare versions
########################################

version_gt() {
    [ "$1" = "$2" ] && return 1

    local IFS=.
    local i ver1=($1) ver2=($2)

    for ((i=${#ver1[@]}; i<${#ver2[@]}; i++)); do ver1[i]=0; done
    for ((i=${#ver2[@]}; i<${#ver1[@]}; i++)); do ver2[i]=0; done

    for ((i=0; i<${#ver1[@]}; i++)); do
        if ((10#${ver1[i]} > 10#${ver2[i]})); then return 0; fi
        if ((10#${ver1[i]} < 10#${ver2[i]})); then return 1; fi
    done

    return 1
}

#echo "Installed: [$installed_version]"
#echo "Latest:    [$latest_version]"

if version_gt "$latest_version" "$installed_version"; then
    echo "Update available."
    send_email "Update successful" "Server: `hostname`\nStatus: Roundcube updated successfully!\nPrevious version: $installed_version\nNew version: $latest_version\nBackup directory: $backup_dir\nTime: $(date)"

elif version_gt "$installed_version" "$latest_version"; then
    echo "Installed version is newer than LTS."
    send_email "Installed version newer than LTS" "Server: `hostname`\nStatus: Installed Roundcube version ($installed_version) is newer than latest LTS ($latest_version).\nNo update performed.\nTime: $(date)"
else
    echo "Latest LTS version:" "$latest_version"
    echo "Already up to date."
    send_email "No update needed" "Server: `hostname`\nStatus: Roundcube is already up to date.\nInstalled version: $installed_version\nLatest LTS: $latest_version\nTime: $(date)"
    exit 0
fi

########################################
# Backup current installation
########################################

backup_dir="roundcube_backup_v${installed_version}_$(date +%F_%H%M%S)"

echo "Creating backup: $backup_dir"
cp -a roundcube "$backup_dir"

########################################
# Download release
########################################

filename=$(basename "$download_url")

echo "Downloading $filename"
curl -fL -o "$filename" "$download_url"

########################################
# Verify SHA256
########################################

echo "Verifying checksum..."
sha256_actual=$(sha256sum "$filename" | awk '{print $1}')

if [[ "$sha256_actual" != "$sha256_expected" ]]; then
    echo "Checksum verification FAILED!"
    rm -f "$filename"
    send_email "Server: `hostname`\nUpdate FAILED" "Status: Roundcube update failed!\nInstalled version: $installed_version\nLatest version: $latest_version\nBackup: $backup_dir\nReason: SHA256 mismatch or extraction failure\nTime: $(date)"
    exit 4
fi

echo "Checksum OK."

########################################
# Extract + Install
########################################

echo "Extracting..."
tar -xzf "$filename"

src_dir="roundcubemail-$latest_version"

if [[ ! -d "$src_dir" ]]; then
    echo "Extraction failed. Directory $src_dir not found."
    exit 5
fi

echo "Running install script..."
yes | "$src_dir/bin/installto.sh" "$INSTALL_DIR"

########################################
# Fix permissions
########################################

chown -R "$OWNER" roundcube

########################################
# Cleanup
########################################

rm -rf "$src_dir" "$filename"

echo "Update completed successfully!"
echo "Now running version: $latest_version"

send_email "Update successful.\nStatus: Roundcube updated: $installed_version -> $latest_version\nBackup: $backup_dir"

Offline
*
Re: Roundcube vulnerability
« Reply #21 on: Today at 07:43:15 AM »
Thanks a lot for sharing the script. Very useful. However I've tried to enhance it. You can use this one, a more robust version attached here:

Code: [Select]
#!/usr/bin/env bash

####################################################################################
#                                                                                  #
#  The MIT License (MIT)                                                           #
#                                                                                  #
#  Copyright (c) 2026 BeinHost.com                                                 #
#                                                                                  #
#  Permission is hereby granted, free of charge, to any person obtaining a copy    #
#  of this software and associated documentation files (the "Software"), to deal   #
#  in the Software without restriction, including without limitation the rights    #
#  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell       #
#  copies of the Software, and to permit persons to whom the Software is           #
#  furnished to do so, subject to the following conditions:                        #
#                                                                                  #
#  The above copyright notice and this permission notice shall be included in all  #
#  copies or substantial portions of the Software.                                 #
#                                                                                  #
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR      #
#  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,        #
#  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE     #
#  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER          #
#  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,   #
#  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE   #
#  SOFTWARE.                                                                       #
#                                                                                  #
####################################################################################
set -euo pipefail
IFS=$'\n\t'

########################################
# CONFIG
########################################

BASE_DIR="/usr/local/cwpsrv/var/services"
INSTALL_DIR="$BASE_DIR/roundcube"
PAGE_URL="https://roundcube.net/download/"
OWNER="cwpsvc:cwpsvc"
EMAIL="mail@domain.com" # set your email address here
ENABLE_NOTIFICATIONS=true    # true/false to enable/disable notifications
SUBJECT_PREFIX="[Roundcube Updater]"
RC_VERSION="lts" # select between "lts" or "stable"
CONNECT_TIMEOUT=10
MAX_TIMEOUT=30

cd "$BASE_DIR" || exit 1

########################################
# INITIAL CHECKS
########################################
required_cmds=(
  curl
  sha256sum
  tar
  mail
  yes
  awk
  grep
  sed
)

for cmd in "${required_cmds[@]}"; do
    if ! command -v "$cmd" >/dev/null 2>&1; then
        echo "Required command not found: $cmd"
        exit 10
    fi
done

exec 9>/var/lock/roundcube_updater.lock
flock -n 9 || { echo "Another update process is running."; exit 20; }

trap 'send_email "Update FAILED (unexpected)" "Server: $(hostname)\nAn unexpected error occurred.\nTime: $(date)"' ERR

########################################
# Send notification
########################################

send_email() {
    if [ "$ENABLE_NOTIFICATIONS" = true ]; then
        local subject="$1"
        local body="$2"

        # use mail command
        echo -e "$body" | mail -s "$SUBJECT_PREFIX $subject" "$EMAIL"
    fi
}

########################################
# Detect installed version
########################################

INI_FILE="$INSTALL_DIR/program/include/iniset.php"

if [[ ! -f "$INI_FILE" ]]; then
    echo "Cannot detect installed version (iniset.php missing)."
    exit 2
fi

installed_version=$(grep -oE "RCMAIL_VERSION',[[:space:]]*'[^']+'" \
    "$INI_FILE" | sed -E "s/.*'([^']+)'.*/\1/")
installed_version=$(echo "$installed_version" | tr -d '\r\n[:space:]')

echo "Installed version: $installed_version"

########################################
# Detect latest version + checksum
########################################

echo "Fetching releases information..."

if ! page_content=$(curl -fsSL --connect-timeout $CONNECT_TIMEOUT --max-time $MAX_TIMEOUT "$PAGE_URL"); then
    echo "Failed to fetch download page."
    send_email "Update FAILED" "Server: $(hostname)\nStatus: Failed to retrieve Roundcube download page.\nURL: $PAGE_URL\nTime: $(date)"

    exit 30
fi

block_id="$RC_VERSION"
version_block=$(echo "$page_content" | awk "/<h2 id=\"$block_id\">/,/<\/table>/")

if [[ -z "$version_block" ]]; then
    echo "Failed to parse the releases block."
    send_email "Update FAILED" "Server: $(hostname)\nStatus: Could not parse the release section from download page.\nURL: $PAGE_URL\nTime: $(date)"

    exit 31
fi

download_url=$(echo "$version_block" \
  | grep -oE 'https://[^"]+-complete\.tar\.gz' \
  | head -n1)

latest_version=$(echo "$download_url" \
  | sed -E 's/.*roundcubemail-([0-9]+\.[0-9]+\.[0-9]+)-complete\.tar\.gz/\1/')
latest_version=$(echo "$latest_version" | tr -d '\r\n[:space:]')

sha256_expected=$(echo "$version_block" \
  | grep -oE '[a-f0-9]{64}' \
  | head -n1)

if [[ -z "$download_url" || -z "$latest_version" || -z "$sha256_expected" ]]; then
    echo "Failed to detect the latest release."
    send_email "Update failed" "Server: $(hostname)\nFailed to detect the latest release.\nTime: $(date)"
    exit 3
fi

########################################
# Compare versions
########################################

version_gt() {
    [ "$1" = "$2" ] && return 1

    local IFS=.
    local i ver1=($1) ver2=($2)

    for ((i=${#ver1[@]}; i<${#ver2[@]}; i++)); do ver1[i]=0; done
    for ((i=${#ver2[@]}; i<${#ver1[@]}; i++)); do ver2[i]=0; done

    for ((i=0; i<${#ver1[@]}; i++)); do
        if ((10#${ver1[i]} > 10#${ver2[i]})); then return 0; fi
        if ((10#${ver1[i]} < 10#${ver2[i]})); then return 1; fi
    done

    return 1
}

#echo "Installed: [$installed_version]"
#echo "Latest:    [$latest_version]"

if version_gt "$latest_version" "$installed_version"; then
    echo "Update available."
   
elif version_gt "$installed_version" "$latest_version"; then
    echo "Installed version is newer than latest available."
    send_email "Installed version newer than available" "Server: $(hostname)\nStatus: Installed Roundcube version ($installed_version) is newer than latest LTS ($latest_version).\nNo update performed.\nTime: $(date)"
    exit 0
else
    echo "Latest version:" "$latest_version"
    echo "Already up to date."
    send_email "No update needed" "Server: $(hostname)\nStatus: Roundcube is already up to date.\nInstalled version: $installed_version\nLatest available: $latest_version\nTime: $(date)"
    exit 0
fi

########################################
# Confirmation only if not in CRON mode
########################################
if [ -t 0 ]; then
    read -rp "Do you want to download and install the new Roundcube release $latest_version? [y/N]: " yn
    case "$yn" in
        [Yy]*) ;;
        *) echo "Update cancelled by user."; exit 0 ;;
    esac
fi

########################################
# Backup current installation
########################################

backup_dir="roundcube_backup_v${installed_version}_$(date +%F_%H%M%S)"

echo "Creating backup: $backup_dir"
cp -a "$INSTALL_DIR" "$backup_dir"

########################################
# Download release
########################################

filename=$(basename "$download_url")

echo "Downloading $filename"

if ! curl -fL --connect-timeout $CONNECT_TIMEOUT --max-time $MAX_TIMEOUT -o "$filename" "$download_url"; then
    echo "Failed to download latest release."
    send_email "Update FAILED" "Server: $(hostname)\nStatus: Failed to download the latest release package.\nURL: $PAGE_URL\nTime: $(date)"

    exit 30
fi

########################################
# Verify SHA256
########################################

echo "Verifying checksum..."
sha256_actual=$(sha256sum "$filename" | awk '{print $1}')

if [[ "$sha256_actual" != "$sha256_expected" ]]; then
    echo "Checksum verification FAILED!"
    rm -f "$filename"
    send_email "Update FAILED" "Server: $(hostname)\nStatus: Roundcube update failed!\nInstalled version: $installed_version\nLatest version: $latest_version\nBackup: $backup_dir\nReason: SHA256 mismatch or extraction failure\nTime: $(date)"
    exit 4
fi

echo "Checksum OK."

########################################
# Extract + Install
########################################

echo "Extracting..."
if ! tar -xzf "$filename"; then
    echo "Extraction failed."
    send_email "Update FAILED" "Server: $(hostname)\nStatus: Extraction failed.\nVersion: $latest_version\nBackup: $backup_dir\nTime: $(date)"
    exit 14
fi

src_dir="roundcubemail-$latest_version"

if [[ ! -d "$src_dir" ]]; then
    echo "Extraction failed. Directory $src_dir not found."
    exit 5
fi

echo "Running install script..."
if ! yes | "$src_dir/bin/installto.sh" "$INSTALL_DIR"; then
    echo "Install failed"
    send_email "Update FAILED" "Server: $(hostname)\nStatus: Installation script failed.\nRollback performed.\nTime: $(date)"
    [[ -d "${INSTALL_DIR}_failed" ]] && mv "${INSTALL_DIR}_failed" "${INSTALL_DIR}_failed_$(date +%s)"
    if ! mv "$INSTALL_DIR" "${INSTALL_DIR}_failed"; then
        echo "Rollback failed (step 1)"
        exit 7
    fi

    if ! mv "$backup_dir" "$INSTALL_DIR"; then
        echo "Rollback failed (step 2)"
        exit 8
    fi
    exit 6
fi

########################################
# Post-install verification
########################################

echo "Verifying installed version..."

if ! new_installed_version=$(grep -oE "RCMAIL_VERSION',[[:space:]]*'[^']+'" \
    "$INI_FILE" | sed -E "s/.*'([^']+)'.*/\1/"); then
    echo "Failed to detect installed version after update."
    exit 13
fi
new_installed_version=$(echo "$new_installed_version" | tr -d '\r\n[:space:]')

if [[ "$new_installed_version" != "$latest_version" ]]; then
    echo "Post-install verification FAILED!"
    echo "Expected: $latest_version"
    echo "Detected: $new_installed_version"

    [[ -d "${INSTALL_DIR}_failed" ]] && mv "${INSTALL_DIR}_failed" "${INSTALL_DIR}_failed_$(date +%s)"
    if ! mv "$INSTALL_DIR" "${INSTALL_DIR}_failed"; then
        echo "Rollback failed (post-check step 1)"
        exit 11
    fi

    if ! mv "$backup_dir" "$INSTALL_DIR"; then
        echo "Rollback failed (post-check step 2)"
        exit 12
    fi

    send_email "Update FAILED" "Server: $(hostname)
Status: Roundcube update failed during post-install verification.
Expected version: $latest_version
Detected version: $new_installed_version
Rollback performed.
Time: $(date)"

    exit 9
fi

echo "Post-install verification OK."

########################################
# Fix permissions
########################################

chown -R "$OWNER" "$INSTALL_DIR"

########################################
# Cleanup
########################################

rm -rf "$src_dir" "$filename"

echo "Update completed successfully!"
echo "Now running version: $latest_version"

send_email "Update successful" "Server: $(hostname)\nStatus: Roundcube updated successfully!\nPrevious version: $installed_version\nNew version: $latest_version\nBackup directory: $backup_dir\nTime: $(date)"

I hope it helps. Cheers!  :)
« Last Edit: Today at 08:23:28 AM by Longhorn »

Offline
*
Re: Roundcube vulnerability
« Reply #22 on: Today at 10:36:01 AM »
Hi again. I've fixed and tweaked some issues. Now it works fine. However, due to CWP limited to PHP 7.4 (!!!!) Roundcube can't be updated beyond 1.6.11 so be careful if you select "stable" and not "lts" into the script settings:

Code: [Select]
#!/usr/bin/env bash

####################################################################################
#                                                                                  #
#  The MIT License (MIT)                                                           #
#                                                                                  #
#  Copyright (c) 2026 BeinHost.com                                                 #
#                                                                                  #
#  Permission is hereby granted, free of charge, to any person obtaining a copy    #
#  of this software and associated documentation files (the "Software"), to deal   #
#  in the Software without restriction, including without limitation the rights    #
#  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell       #
#  copies of the Software, and to permit persons to whom the Software is           #
#  furnished to do so, subject to the following conditions:                        #
#                                                                                  #
#  The above copyright notice and this permission notice shall be included in all  #
#  copies or substantial portions of the Software.                                 #
#                                                                                  #
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR      #
#  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,        #
#  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE     #
#  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER          #
#  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,   #
#  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE   #
#  SOFTWARE.                                                                       #
#                                                                                  #
####################################################################################
set -euo pipefail
IFS=$'\n\t'

########################################
# CONFIG
########################################

BASE_DIR="/usr/local/cwpsrv/var/services"
INSTALL_DIR="$BASE_DIR/roundcube"
PAGE_URL="https://roundcube.net/download/"
OWNER="cwpsvc:cwpsvc"
EMAIL="mail@domain.com" # set your email address here
ENABLE_NOTIFICATIONS=true    # true/false to enable/disable notifications
SUBJECT_PREFIX="[Roundcube Updater]"
RC_VERSION="lts" # select between "lts" or "stable" or "beta"
CONNECT_TIMEOUT=10
MAX_TIMEOUT=30

cd "$BASE_DIR" || exit 1

########################################
# INITIAL CHECKS
########################################
required_cmds=(
  curl
  sha256sum
  tar
  mail
  yes
  awk
  grep
  sed
)

for cmd in "${required_cmds[@]}"; do
    if ! command -v "$cmd" >/dev/null 2>&1; then
        echo "Required command not found: $cmd"
        exit 10
    fi
done

exec 9>/var/lock/roundcube_updater.lock
flock -n 9 || { echo "Another update process is running."; exit 20; }

trap 'send_email "Update FAILED (unexpected)" "Server: $(hostname)\nAn unexpected error occurred.\nTime: $(date)"' ERR

########################################
# Send notification
########################################

send_email() {
    if [ "$ENABLE_NOTIFICATIONS" = true ]; then
        local subject="$1"
        local body="$2"

        # use mail command
        echo -e "$body" | mail -s "$SUBJECT_PREFIX $subject" "$EMAIL"
    fi
}

########################################
# Detect installed version
########################################

INI_FILE="$INSTALL_DIR/program/include/iniset.php"

if [[ ! -f "$INI_FILE" ]]; then
    echo "Cannot detect installed version (iniset.php missing)."
    exit 2
fi

installed_version=$(grep -oE "RCMAIL_VERSION',[[:space:]]*'[^']+'" \
    "$INI_FILE" | sed -E "s/.*'([^']+)'.*/\1/")
installed_version=$(echo "$installed_version" | tr -d '\r\n[:space:]')

echo "Installed version: $installed_version"

########################################
# Detect latest version + checksum
########################################

echo "Fetching releases information..."

if ! page_content=$(curl -fsSL --connect-timeout $CONNECT_TIMEOUT --max-time $MAX_TIMEOUT "$PAGE_URL"); then
    echo "Failed to fetch download page."
    send_email "Update FAILED" "Server: $(hostname)\nStatus: Failed to retrieve Roundcube download page.\nURL: $PAGE_URL\nTime: $(date)"

    exit 30
fi

block_id="$RC_VERSION"
version_block=$(echo "$page_content" | awk "/<h2 id=\"$block_id\">/,/<\/table>/")

if [[ -z "$version_block" ]]; then
    echo "Failed to parse the releases block."
    send_email "Update FAILED" "Server: $(hostname)\nStatus: Could not parse the release section from download page.\nURL: $PAGE_URL\nTime: $(date)"

    exit 31
fi

row_complete=$(echo "$version_block" | awk '
    BEGIN {RS="</tr>"; FS="\n"}
    /roundcubemail-[0-9]+\.[0-9]+\.[0-9]+-complete\.tar\.gz/ {print $0 "</tr>"}' || true)

if [[ -z "$row_complete" ]]; then
    echo "Failed to find the complete release row."
    send_email "Update FAILED" "Server: $(hostname)\nStatus: Could not find the complete release row for $RC_VERSION.\nTime: $(date)"
    exit 32
fi

download_url=$(echo "$row_complete" | grep -oE 'https://[^"]+-complete\.tar\.gz' | head -n1 || true)
latest_version=$(echo "$download_url" | sed -E 's/.*roundcubemail-([0-9]+\.[0-9]+\.[0-9]+)-complete\.tar\.gz/\1/' | tr -d '\r\n[:space:]' || true)
sha256_expected=$(echo "$row_complete" | grep -oP '<span class="copy-target[^"]*">\K[a-f0-9]{64}' || true)

if [[ -z "$download_url" || -z "$latest_version" || -z "$sha256_expected" ]]; then
    echo "Failed to detect the latest release or its checksum."
    send_email "Update failed" "Server: $(hostname)\nFailed to detect the latest release or checksum.\nTime: $(date)"
    exit 3
fi

########################################
# Compare versions
########################################

version_gt() {
    [ "$1" = "$2" ] && return 1

    local IFS=.
    local i ver1=($1) ver2=($2)

    for ((i=${#ver1[@]}; i<${#ver2[@]}; i++)); do ver1[i]=0; done
    for ((i=${#ver2[@]}; i<${#ver1[@]}; i++)); do ver2[i]=0; done

    for ((i=0; i<${#ver1[@]}; i++)); do
        if ((10#${ver1[i]} > 10#${ver2[i]})); then return 0; fi
        if ((10#${ver1[i]} < 10#${ver2[i]})); then return 1; fi
    done

    return 1
}

#echo "Installed: [$installed_version]"
#echo "Latest:    [$latest_version]"

if version_gt "$latest_version" "$installed_version"; then
    echo "Update available."
   
elif version_gt "$installed_version" "$latest_version"; then
    echo "Installed version is newer than latest available ($latest_version)."
    send_email "Installed version newer than available" "Server: $(hostname)\nStatus: Installed Roundcube version ($installed_version) is newer than latest LTS ($latest_version).\nNo update performed.\nTime: $(date)"
    exit 0
else
    echo "Latest version:" "$latest_version"
    echo "Already up to date."
    send_email "No update needed" "Server: $(hostname)\nStatus: Roundcube is already up to date.\nInstalled version: $installed_version\nLatest available: $latest_version\nTime: $(date)"
    exit 0
fi

########################################
# Confirmation only if not in CRON mode
########################################
if [ -t 0 ]; then
    read -rp "Do you want to download and install the new Roundcube release $latest_version? [y/N]: " yn
    case "$yn" in
        [Yy]*) ;;
        *) echo "Update cancelled by user."; exit 0 ;;
    esac
fi

########################################
# Backup current installation
########################################

backup_dir="roundcube_backup_v${installed_version}_$(date +%F_%H%M%S)"

echo "Creating backup: $backup_dir"
cp -a "$INSTALL_DIR" "$backup_dir"

########################################
# Download release
########################################

filename=$(basename "$download_url")

echo "Downloading $filename"

if ! curl -fL --connect-timeout $CONNECT_TIMEOUT --max-time $MAX_TIMEOUT -o "$filename" "$download_url"; then
    echo "Failed to download latest release."
    send_email "Update FAILED" "Server: $(hostname)\nStatus: Failed to download the latest release package.\nURL: $PAGE_URL\nTime: $(date)"

    exit 30
fi

########################################
# Verify SHA256
########################################

echo "Verifying checksum..."
sha256_actual=$(sha256sum "$filename" | awk '{print $1}')

if [[ "$sha256_actual" != "$sha256_expected" ]]; then
    echo "Checksum verification FAILED!"
    rm -f "$filename"
    send_email "Update FAILED" "Server: $(hostname)\nStatus: Roundcube update failed!\nInstalled version: $installed_version\nLatest version: $latest_version\nBackup: $backup_dir\nReason: SHA256 mismatch or extraction failure\nTime: $(date)"
    exit 4
fi

echo "Checksum OK."

########################################
# Extract + Install
########################################

echo "Extracting..."
if ! tar -xzf "$filename"; then
    echo "Extraction failed."
    send_email "Update FAILED" "Server: $(hostname)\nStatus: Extraction failed.\nVersion: $latest_version\nBackup: $backup_dir\nTime: $(date)"
    exit 14
fi

src_dir="roundcubemail-$latest_version"

if [[ ! -d "$src_dir" ]]; then
    echo "Extraction failed. Directory $src_dir not found."
    exit 5
fi

echo "Running install script..."
if [ -t 0 ]; then
    "$src_dir/bin/installto.sh" "$INSTALL_DIR"
else
    yes | "$src_dir/bin/installto.sh" "$INSTALL_DIR"
fi

if [[ $? -ne 0 ]]; then
    echo "Install failed"
    send_email "Update FAILED" "Server: $(hostname)\nStatus: Installation script failed.\nRollback performed.\nTime: $(date)"
    [[ -d "${INSTALL_DIR}_failed" ]] && mv "${INSTALL_DIR}_failed" "${INSTALL_DIR}_failed_$(date +%s)"
    if ! mv "$INSTALL_DIR" "${INSTALL_DIR}_failed"; then
        echo "Rollback failed (step 1)"
        exit 7
    fi

    if ! mv "$backup_dir" "$INSTALL_DIR"; then
        echo "Rollback failed (step 2)"
        exit 8
    fi
    exit 6
fi

########################################
# Post-install verification
########################################

echo "Verifying installed version..."

if ! new_installed_version=$(grep -oE "RCMAIL_VERSION',[[:space:]]*'[^']+'" \
    "$INI_FILE" | sed -E "s/.*'([^']+)'.*/\1/"); then
    echo "Failed to detect installed version after update."
    exit 13
fi
new_installed_version=$(echo "$new_installed_version" | tr -d '\r\n[:space:]')

if [[ "$new_installed_version" != "$latest_version" ]]; then
    echo "Post-install verification FAILED!"
    echo "Expected: $latest_version"
    echo "Detected: $new_installed_version"

    [[ -d "${INSTALL_DIR}_failed" ]] && mv "${INSTALL_DIR}_failed" "${INSTALL_DIR}_failed_$(date +%s)"
    if ! mv "$INSTALL_DIR" "${INSTALL_DIR}_failed"; then
        echo "Rollback failed (post-check step 1)"
        exit 11
    fi

    if ! mv "$backup_dir" "$INSTALL_DIR"; then
        echo "Rollback failed (post-check step 2)"
        exit 12
    fi

    send_email "Update FAILED" "Server: $(hostname)\nStatus: Roundcube update failed during post-install verification.\nExpected version: $latest_version\nDetected version: $new_installed_version\nRollback performed.\nTime: $(date)"

    exit 9
fi

echo "Post-install verification OK."

########################################
# Fix permissions
########################################

chown -R "$OWNER" "$INSTALL_DIR"

########################################
# Cleanup
########################################

rm -rf "$src_dir" "$filename"

echo "Update completed successfully!"
echo "Now running version: $latest_version"

send_email "Update successful" "Server: $(hostname)\nStatus: Roundcube updated successfully!\nPrevious version: $installed_version\nNew version: $latest_version\nBackup directory: $backup_dir\nTime: $(date)"

Offline
*
Re: Roundcube vulnerability
« Reply #23 on: Today at 01:14:59 PM »
(...) due to CWP limited to PHP 7.4 (!!!!) Roundcube can't be updated beyond 1.6.11 (...)


"everything is fine, CWP have received updates X days ago"


Thank you for your script and alert.