From 4352fffeec7915e45559b46dce18640a25f46801 Mon Sep 17 00:00:00 2001 From: Mathijs van Veluw Date: Fri, 9 Jan 2026 12:21:10 +0000 Subject: [PATCH] Fix web-vault version check and update web-vault (#6686) --- docker/DockerSettings.yaml | 4 +- docker/Dockerfile.alpine | 12 ++-- docker/Dockerfile.debian | 12 ++-- docker/Dockerfile.j2 | 6 +- src/api/admin.rs | 78 ++++++++++++++++------ src/config.rs | 4 +- src/main.rs | 2 +- src/static/scripts/admin_diagnostics.js | 12 ++-- src/static/templates/admin/diagnostics.hbs | 4 +- src/util.rs | 2 +- 10 files changed, 87 insertions(+), 49 deletions(-) diff --git a/docker/DockerSettings.yaml b/docker/DockerSettings.yaml index e74f979c..dd87a9e3 100644 --- a/docker/DockerSettings.yaml +++ b/docker/DockerSettings.yaml @@ -1,6 +1,6 @@ --- -vault_version: "v2025.12.1.1" -vault_image_digest: "sha256:90261e5d5438b67a00cb12d8615cf3f130a65e81f33a3f5ff190c6202bf0e457" +vault_version: "v2025.12.1+build.3" +vault_image_digest: "sha256:bf5aa55dc7bcb99f85d2a88ff44d32cdc832e934a0603fe28e5c3f92904bad42" # Cross Compile Docker Helper Scripts v1.9.0 # We use the linux/amd64 platform shell scripts since there is no difference between the different platform scripts # https://github.com/tonistiigi/xx | https://hub.docker.com/r/tonistiigi/xx/tags diff --git a/docker/Dockerfile.alpine b/docker/Dockerfile.alpine index 6453ba1f..2a6cf9f2 100644 --- a/docker/Dockerfile.alpine +++ b/docker/Dockerfile.alpine @@ -19,15 +19,15 @@ # - From https://hub.docker.com/r/vaultwarden/web-vault/tags, # click the tag name to view the digest of the image it currently points to. # - From the command line: -# $ docker pull docker.io/vaultwarden/web-vault:v2025.12.1.1 -# $ docker image inspect --format "{{.RepoDigests}}" docker.io/vaultwarden/web-vault:v2025.12.1.1 -# [docker.io/vaultwarden/web-vault@sha256:90261e5d5438b67a00cb12d8615cf3f130a65e81f33a3f5ff190c6202bf0e457] +# $ docker pull docker.io/vaultwarden/web-vault:v2025.12.1_build.3 +# $ docker image inspect --format "{{.RepoDigests}}" docker.io/vaultwarden/web-vault:v2025.12.1_build.3 +# [docker.io/vaultwarden/web-vault@sha256:bf5aa55dc7bcb99f85d2a88ff44d32cdc832e934a0603fe28e5c3f92904bad42] # # - Conversely, to get the tag name from the digest: -# $ docker image inspect --format "{{.RepoTags}}" docker.io/vaultwarden/web-vault@sha256:90261e5d5438b67a00cb12d8615cf3f130a65e81f33a3f5ff190c6202bf0e457 -# [docker.io/vaultwarden/web-vault:v2025.12.1.1] +# $ docker image inspect --format "{{.RepoTags}}" docker.io/vaultwarden/web-vault@sha256:bf5aa55dc7bcb99f85d2a88ff44d32cdc832e934a0603fe28e5c3f92904bad42 +# [docker.io/vaultwarden/web-vault:v2025.12.1_build.3] # -FROM --platform=linux/amd64 docker.io/vaultwarden/web-vault@sha256:90261e5d5438b67a00cb12d8615cf3f130a65e81f33a3f5ff190c6202bf0e457 AS vault +FROM --platform=linux/amd64 docker.io/vaultwarden/web-vault@sha256:bf5aa55dc7bcb99f85d2a88ff44d32cdc832e934a0603fe28e5c3f92904bad42 AS vault ########################## ALPINE BUILD IMAGES ########################## ## NOTE: The Alpine Base Images do not support other platforms then linux/amd64 and linux/arm64 diff --git a/docker/Dockerfile.debian b/docker/Dockerfile.debian index 25545f32..03c0faba 100644 --- a/docker/Dockerfile.debian +++ b/docker/Dockerfile.debian @@ -19,15 +19,15 @@ # - From https://hub.docker.com/r/vaultwarden/web-vault/tags, # click the tag name to view the digest of the image it currently points to. # - From the command line: -# $ docker pull docker.io/vaultwarden/web-vault:v2025.12.1.1 -# $ docker image inspect --format "{{.RepoDigests}}" docker.io/vaultwarden/web-vault:v2025.12.1.1 -# [docker.io/vaultwarden/web-vault@sha256:90261e5d5438b67a00cb12d8615cf3f130a65e81f33a3f5ff190c6202bf0e457] +# $ docker pull docker.io/vaultwarden/web-vault:v2025.12.1_build.3 +# $ docker image inspect --format "{{.RepoDigests}}" docker.io/vaultwarden/web-vault:v2025.12.1_build.3 +# [docker.io/vaultwarden/web-vault@sha256:bf5aa55dc7bcb99f85d2a88ff44d32cdc832e934a0603fe28e5c3f92904bad42] # # - Conversely, to get the tag name from the digest: -# $ docker image inspect --format "{{.RepoTags}}" docker.io/vaultwarden/web-vault@sha256:90261e5d5438b67a00cb12d8615cf3f130a65e81f33a3f5ff190c6202bf0e457 -# [docker.io/vaultwarden/web-vault:v2025.12.1.1] +# $ docker image inspect --format "{{.RepoTags}}" docker.io/vaultwarden/web-vault@sha256:bf5aa55dc7bcb99f85d2a88ff44d32cdc832e934a0603fe28e5c3f92904bad42 +# [docker.io/vaultwarden/web-vault:v2025.12.1_build.3] # -FROM --platform=linux/amd64 docker.io/vaultwarden/web-vault@sha256:90261e5d5438b67a00cb12d8615cf3f130a65e81f33a3f5ff190c6202bf0e457 AS vault +FROM --platform=linux/amd64 docker.io/vaultwarden/web-vault@sha256:bf5aa55dc7bcb99f85d2a88ff44d32cdc832e934a0603fe28e5c3f92904bad42 AS vault ########################## Cross Compile Docker Helper Scripts ########################## ## We use the linux/amd64 no matter which Build Platform, since these are all bash scripts diff --git a/docker/Dockerfile.j2 b/docker/Dockerfile.j2 index cf8106bd..f745780e 100644 --- a/docker/Dockerfile.j2 +++ b/docker/Dockerfile.j2 @@ -19,13 +19,13 @@ # - From https://hub.docker.com/r/vaultwarden/web-vault/tags, # click the tag name to view the digest of the image it currently points to. # - From the command line: -# $ docker pull docker.io/vaultwarden/web-vault:{{ vault_version }} -# $ docker image inspect --format "{{ '{{' }}.RepoDigests}}" docker.io/vaultwarden/web-vault:{{ vault_version }} +# $ docker pull docker.io/vaultwarden/web-vault:{{ vault_version | replace('+', '_') }} +# $ docker image inspect --format "{{ '{{' }}.RepoDigests}}" docker.io/vaultwarden/web-vault:{{ vault_version | replace('+', '_') }} # [docker.io/vaultwarden/web-vault@{{ vault_image_digest }}] # # - Conversely, to get the tag name from the digest: # $ docker image inspect --format "{{ '{{' }}.RepoTags}}" docker.io/vaultwarden/web-vault@{{ vault_image_digest }} -# [docker.io/vaultwarden/web-vault:{{ vault_version }}] +# [docker.io/vaultwarden/web-vault:{{ vault_version | replace('+', '_') }}] # FROM --platform=linux/amd64 docker.io/vaultwarden/web-vault@{{ vault_image_digest }} AS vault diff --git a/src/api/admin.rs b/src/api/admin.rs index d36da8f9..badfaa3a 100644 --- a/src/api/admin.rs +++ b/src/api/admin.rs @@ -31,7 +31,7 @@ use crate::{ http_client::make_http_request, mail, util::{ - container_base_image, format_naive_datetime_local, get_display_size, get_web_vault_version, + container_base_image, format_naive_datetime_local, get_active_web_release, get_display_size, is_running_in_container, NumberOrString, }, CONFIG, VERSION, @@ -689,6 +689,26 @@ async fn get_ntp_time(has_http_access: bool) -> String { String::from("Unable to fetch NTP time.") } +fn web_vault_compare(active: &str, latest: &str) -> i8 { + use semver::Version; + use std::cmp::Ordering; + + let active_semver = Version::parse(active).unwrap_or_else(|e| { + warn!("Unable to parse active web-vault version '{active}': {e}"); + Version::parse("2025.1.1").unwrap() + }); + let latest_semver = Version::parse(latest).unwrap_or_else(|e| { + warn!("Unable to parse latest web-vault version '{latest}': {e}"); + Version::parse("2025.1.1").unwrap() + }); + + match active_semver.cmp(&latest_semver) { + Ordering::Less => -1, + Ordering::Equal => 0, + Ordering::Greater => 1, + } +} + #[get("/diagnostics")] async fn diagnostics(_token: AdminToken, ip_header: IpHeader, conn: DbConn) -> ApiResult> { use chrono::prelude::*; @@ -708,32 +728,21 @@ async fn diagnostics(_token: AdminToken, ip_header: IpHeader, conn: DbConn) -> A _ => "Unable to resolve domain name.".to_string(), }; - let (latest_release, latest_commit, latest_web_build) = get_release_info(has_http_access).await; + let (latest_vw_release, latest_vw_commit, latest_web_release) = get_release_info(has_http_access).await; + let active_web_release = get_active_web_release(); + let web_vault_compare = web_vault_compare(&active_web_release, &latest_web_release); let ip_header_name = &ip_header.0.unwrap_or_default(); - // Get current running versions - let web_vault_version = get_web_vault_version(); - - // Check if the running version is newer than the latest stable released version - let web_vault_pre_release = if let Ok(web_ver_match) = semver::VersionReq::parse(&format!(">{latest_web_build}")) { - web_ver_match.matches( - &semver::Version::parse(&web_vault_version).unwrap_or_else(|_| semver::Version::parse("2025.1.1").unwrap()), - ) - } else { - error!("Unable to parse latest_web_build: '{latest_web_build}'"); - false - }; - let diagnostics_json = json!({ "dns_resolved": dns_resolved, "current_release": VERSION, - "latest_release": latest_release, - "latest_commit": latest_commit, + "latest_release": latest_vw_release, + "latest_commit": latest_vw_commit, "web_vault_enabled": &CONFIG.web_vault_enabled(), - "web_vault_version": web_vault_version, - "latest_web_build": latest_web_build, - "web_vault_pre_release": web_vault_pre_release, + "active_web_release": active_web_release, + "latest_web_release": latest_web_release, + "web_vault_compare": web_vault_compare, "running_within_container": running_within_container, "container_base_image": if running_within_container { container_base_image() } else { "Not applicable" }, "has_http_access": has_http_access, @@ -844,3 +853,32 @@ impl<'r> FromRequest<'r> for AdminToken { }) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn validate_web_vault_compare() { + // web_vault_compare(active, latest) + // Test normal versions + assert!(web_vault_compare("2025.12.0", "2025.12.1") == -1); + assert!(web_vault_compare("2025.12.1", "2025.12.1") == 0); + assert!(web_vault_compare("2025.12.2", "2025.12.1") == 1); + + // Test patched/+build.n versions + // Newer latest version + assert!(web_vault_compare("2025.12.0+build.1", "2025.12.1") == -1); + assert!(web_vault_compare("2025.12.1", "2025.12.1+build.1") == -1); + assert!(web_vault_compare("2025.12.0+build.1", "2025.12.1+build.1") == -1); + assert!(web_vault_compare("2025.12.1+build.1", "2025.12.1+build.2") == -1); + // Equal versions + assert!(web_vault_compare("2025.12.1+build.1", "2025.12.1+build.1") == 0); + assert!(web_vault_compare("2025.12.2+build.2", "2025.12.2+build.2") == 0); + // Newer active version + assert!(web_vault_compare("2025.12.1+build.1", "2025.12.1") == 1); + assert!(web_vault_compare("2025.12.2", "2025.12.1+build.1") == 1); + assert!(web_vault_compare("2025.12.2+build.1", "2025.12.1+build.1") == 1); + assert!(web_vault_compare("2025.12.1+build.3", "2025.12.1+build.2") == 1); + } +} diff --git a/src/config.rs b/src/config.rs index 6bfdea80..4fb103fa 100644 --- a/src/config.rs +++ b/src/config.rs @@ -14,7 +14,7 @@ use serde::de::{self, Deserialize, Deserializer, MapAccess, Visitor}; use crate::{ error::Error, - util::{get_env, get_env_bool, get_web_vault_version, is_valid_email, parse_experimental_client_feature_flags}, + util::{get_active_web_release, get_env, get_env_bool, is_valid_email, parse_experimental_client_feature_flags}, }; static CONFIG_FILE: LazyLock = LazyLock::new(|| { @@ -1849,7 +1849,7 @@ fn to_json<'reg, 'rc>( // Configure the web-vault version as an integer so it can be used as a comparison smaller or greater then. // The default is based upon the version since this feature is added. static WEB_VAULT_VERSION: LazyLock = LazyLock::new(|| { - let vault_version = get_web_vault_version(); + let vault_version = get_active_web_release(); // Use a single regex capture to extract version components let re = regex::Regex::new(r"(\d{4})\.(\d{1,2})\.(\d{1,2})").unwrap(); re.captures(&vault_version) diff --git a/src/main.rs b/src/main.rs index b5ff93ae..8eef2e8c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -126,7 +126,7 @@ fn parse_args() { exit(0); } else if pargs.contains(["-v", "--version"]) { config::SKIP_CONFIG_VALIDATION.store(true, Ordering::Relaxed); - let web_vault_version = util::get_web_vault_version(); + let web_vault_version = util::get_active_web_release(); println!("Vaultwarden {version}"); println!("Web-Vault {web_vault_version}"); exit(0); diff --git a/src/static/scripts/admin_diagnostics.js b/src/static/scripts/admin_diagnostics.js index 108034dd..5594b439 100644 --- a/src/static/scripts/admin_diagnostics.js +++ b/src/static/scripts/admin_diagnostics.js @@ -29,7 +29,7 @@ function isValidIp(ip) { return ipv4Regex.test(ip) || ipv6Regex.test(ip); } -function checkVersions(platform, installed, latest, commit=null, pre_release=false) { +function checkVersions(platform, installed, latest, commit=null, compare_order=0) { if (installed === "-" || latest === "-") { document.getElementById(`${platform}-failed`).classList.remove("d-none"); return; @@ -37,7 +37,7 @@ function checkVersions(platform, installed, latest, commit=null, pre_release=fal // Only check basic versions, no commit revisions if (commit === null || installed.indexOf("-") === -1) { - if (platform === "web" && pre_release === true) { + if (platform === "web" && compare_order === 1) { document.getElementById(`${platform}-prerelease`).classList.remove("d-none"); } else if (installed == latest) { document.getElementById(`${platform}-success`).classList.remove("d-none"); @@ -83,7 +83,7 @@ async function generateSupportString(event, dj) { let supportString = "### Your environment (Generated via diagnostics page)\n\n"; supportString += `* Vaultwarden version: v${dj.current_release}\n`; - supportString += `* Web-vault version: v${dj.web_vault_version}\n`; + supportString += `* Web-vault version: v${dj.active_web_release}\n`; supportString += `* OS/Arch: ${dj.host_os}/${dj.host_arch}\n`; supportString += `* Running within a container: ${dj.running_within_container} (Base: ${dj.container_base_image})\n`; supportString += `* Database type: ${dj.db_type}\n`; @@ -208,9 +208,9 @@ function initVersionCheck(dj) { } checkVersions("server", serverInstalled, serverLatest, serverLatestCommit); - const webInstalled = dj.web_vault_version; - const webLatest = dj.latest_web_build; - checkVersions("web", webInstalled, webLatest, null, dj.web_vault_pre_release); + const webInstalled = dj.active_web_release; + const webLatest = dj.latest_web_release; + checkVersions("web", webInstalled, webLatest, null, dj.web_vault_compare); } function checkDns(dns_resolved) { diff --git a/src/static/templates/admin/diagnostics.hbs b/src/static/templates/admin/diagnostics.hbs index 503c6954..f8edabb2 100644 --- a/src/static/templates/admin/diagnostics.hbs +++ b/src/static/templates/admin/diagnostics.hbs @@ -27,13 +27,13 @@ Pre-Release
- {{page_data.web_vault_version}} + {{page_data.active_web_release}}
Web Latest Unknown
- {{page_data.latest_web_build}} + {{page_data.latest_web_release}}
{{/if}} {{#unless page_data.web_vault_enabled}} diff --git a/src/util.rs b/src/util.rs index c7ba9ed1..aa4e7914 100644 --- a/src/util.rs +++ b/src/util.rs @@ -531,7 +531,7 @@ struct WebVaultVersion { version: String, } -pub fn get_web_vault_version() -> String { +pub fn get_active_web_release() -> String { let version_files = [ format!("{}/vw-version.json", CONFIG.web_vault_folder()), format!("{}/version.json", CONFIG.web_vault_folder()),