diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml index c195d24859..b688228aa1 100644 --- a/.github/workflows/build-docker.yml +++ b/.github/workflows/build-docker.yml @@ -72,7 +72,7 @@ jobs: cache-to: type=registry,mode=max,ref=${{ env.GHCR_REPO }}:cache-${{ matrix.tag }}-${{ env.SAFE_REF }} - name: Scan image with Trivy - uses: aquasecurity/trivy-action@0.34.2 + uses: aquasecurity/trivy-action@0.35.0 with: image-ref: "${{ env.GHCR_REPO }}:${{ github.sha }}-${{ matrix.tag }}" format: "table" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0dbe36fcfa..984278ba57 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,7 +5,7 @@ on: branches: - main - dev - - 'release/**' + - "release/**" paths-ignore: - "*.md" - "LICENSE" @@ -13,7 +13,7 @@ on: branches: - main - dev - - 'release/**' + - "release/**" paths-ignore: - "*.md" - "LICENSE" @@ -56,10 +56,10 @@ jobs: submodules: recursive - name: Scan code with Trivy - uses: aquasecurity/trivy-action@0.34.2 + uses: aquasecurity/trivy-action@0.35.0 with: - scan-type: 'fs' - scan-ref: '.' + scan-type: "fs" + scan-ref: "." exit-code: "1" ignore-unfixed: true severity: "CRITICAL,HIGH,MEDIUM" diff --git a/.github/workflows/sbom.yml b/.github/workflows/sbom.yml index 306f8677e3..bebf5e6c66 100644 --- a/.github/workflows/sbom.yml +++ b/.github/workflows/sbom.yml @@ -35,43 +35,43 @@ jobs: submodules: recursive - name: Create SBOM with Trivy - uses: aquasecurity/trivy-action@0.34.2 + uses: aquasecurity/trivy-action@0.35.0 with: - scan-type: 'fs' - format: 'spdx-json' + scan-type: "fs" + format: "spdx-json" output: "defguard-${{ steps.vars.outputs.VERSION }}.sbom.json" - scan-ref: '.' + scan-ref: "." severity: "CRITICAL,HIGH,MEDIUM,LOW" scanners: "vuln" skip-dirs: "e2e" - name: Create docker image SBOM with Trivy - uses: aquasecurity/trivy-action@0.34.2 + uses: aquasecurity/trivy-action@0.35.0 with: image-ref: "ghcr.io/defguard/defguard:${{ steps.vars.outputs.VERSION }}" - scan-type: 'image' - format: 'spdx-json' + scan-type: "image" + format: "spdx-json" output: "defguard-${{ steps.vars.outputs.VERSION }}-docker.sbom.json" severity: "CRITICAL,HIGH,MEDIUM,LOW" scanners: "vuln" - name: Create security advisory file with Trivy - uses: aquasecurity/trivy-action@0.34.2 + uses: aquasecurity/trivy-action@0.35.0 with: - scan-type: 'fs' - format: 'json' + scan-type: "fs" + format: "json" output: "defguard-${{ steps.vars.outputs.VERSION }}.advisories.json" - scan-ref: '.' + scan-ref: "." severity: "CRITICAL,HIGH,MEDIUM,LOW" scanners: "vuln" skip-dirs: "e2e" - name: Create docker image security advisory file with Trivy - uses: aquasecurity/trivy-action@0.34.2 + uses: aquasecurity/trivy-action@0.35.0 with: image-ref: "ghcr.io/defguard/defguard:${{ steps.vars.outputs.VERSION }}" - scan-type: 'image' - format: 'json' + scan-type: "image" + format: "json" output: "defguard-${{ steps.vars.outputs.VERSION }}-docker.advisories.json" severity: "CRITICAL,HIGH,MEDIUM,LOW" scanners: "vuln" diff --git a/Cargo.lock b/Cargo.lock index 5f858ba6b5..c8c85a8b5a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4488,9 +4488,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.9" +version = "0.103.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef" dependencies = [ "ring", "rustls-pki-types", diff --git a/crates/defguard_core/src/enterprise/ldap/error.rs b/crates/defguard_core/src/enterprise/ldap/error.rs index bec3932cf6..65edf30896 100644 --- a/crates/defguard_core/src/enterprise/ldap/error.rs +++ b/crates/defguard_core/src/enterprise/ldap/error.rs @@ -1,10 +1,21 @@ use sqlx::error::Error as SqlxError; use thiserror::Error; +/// LDAP server responses (especially `LdapResult.text` and `LdapResult.matched`) may contain +/// null bytes and non-printable control characters that corrupt log output. This function +/// filters out all control characters except `\n` and `\t`. +pub(super) fn sanitize_ldap_string(s: &str) -> String { + s.chars() + .filter(|c| !c.is_control() || *c == '\n' || *c == '\t') + .collect() +} + #[derive(Debug, Error)] pub enum LdapError { + // Stores a sanitized string to avoid null bytes / control chars from LDAP responses + // corrupting log output. #[error("LDAP error: {0}")] - Ldap(#[from] ldap3::LdapError), + Ldap(String), #[error("Object not found: {0}")] ObjectNotFound(String), #[error("Missing required LDAP settings: {0}")] @@ -31,3 +42,41 @@ pub enum LdapError { #[error("User {0} does not belong to the defined synchronization groups in {1}")] UserNotInLDAPSyncGroups(String, &'static str), } + +impl From for LdapError { + fn from(err: ldap3::LdapError) -> Self { + Self::Ldap(sanitize_ldap_string(&err.to_string())) + } +} + +#[cfg(test)] +mod tests { + use super::sanitize_ldap_string; + + #[test] + fn sanitize_ldap_string_strips_control_chars() { + // Null bytes are stripped + assert_eq!(sanitize_ldap_string("hello\0world"), "helloworld"); + + // Other non-printable control chars are stripped + assert_eq!(sanitize_ldap_string("text\x01\x02\x03"), "text"); + + // Realistic LDAP error string containing null bytes is cleaned correctly + let dirty = "80090308: LdapErr: DSID-0C09044E, comment: AcceptSecurityContext error, data 52e, v2580\0"; + let clean = "80090308: LdapErr: DSID-0C09044E, comment: AcceptSecurityContext error, data 52e, v2580"; + assert_eq!(sanitize_ldap_string(dirty), clean); + + // \n and \t are preserved + assert_eq!( + sanitize_ldap_string("line1\nline2\ttabbed"), + "line1\nline2\ttabbed" + ); + + // Normal ASCII and Unicode pass through unchanged + assert_eq!(sanitize_ldap_string("hello world"), "hello world"); + assert_eq!( + sanitize_ldap_string("zażółć gęślą jaźń"), + "zażółć gęślą jaźń" + ); + } +} diff --git a/crates/defguard_core/src/enterprise/ldap/mod.rs b/crates/defguard_core/src/enterprise/ldap/mod.rs index b3e97a5564..7b7b8182ae 100644 --- a/crates/defguard_core/src/enterprise/ldap/mod.rs +++ b/crates/defguard_core/src/enterprise/ldap/mod.rs @@ -15,7 +15,7 @@ use rand::Rng; use sqlx::PgPool; use sync::{get_ldap_sync_status, is_ldap_desynced, set_ldap_sync_status}; -use self::error::LdapError; +use self::error::{LdapError, sanitize_ldap_string}; use crate::{ db::{self, User}, enterprise::{is_business_license_active, ldap::model::extract_dn_path, limits::update_counts}, @@ -582,7 +582,9 @@ impl LDAPConnection { info!("Found LDAP user with DN: {}", dn); User::from_searchentry(&entry, &user.username, None) } - None => Err(LdapError::ObjectNotFound(format!("User {dn} not found",))), + None => Err(LdapError::ObjectNotFound(sanitize_ldap_string(&format!( + "User {dn} not found", + )))), } } @@ -626,9 +628,11 @@ impl LDAPConnection { if !self.is_username_available(&user.username).await? || self.user_exists_by_dn(&user_dn).await? { - return Err(LdapError::ObjectAlreadyExists(format!( - "User with username {} or DN {user_dn} already exists", - user.username + return Err(LdapError::ObjectAlreadyExists(sanitize_ldap_string( + &format!( + "User with username {} or DN {user_dn} already exists", + user.username + ), ))); } self.add( @@ -704,9 +708,9 @@ impl LDAPConnection { .and_then(|v| v.first()) .map(ToString::to_string) .ok_or_else(|| { - LdapError::ObjectNotFound(format!( + LdapError::ObjectNotFound(sanitize_ldap_string(&format!( "Couldn't extract a group name from searchentry {entry:?}." - )) + ))) }) } diff --git a/crates/defguard_core/src/enterprise/ldap/model.rs b/crates/defguard_core/src/enterprise/ldap/model.rs index 0da8afc1c7..9af03db4e3 100644 --- a/crates/defguard_core/src/enterprise/ldap/model.rs +++ b/crates/defguard_core/src/enterprise/ldap/model.rs @@ -4,7 +4,10 @@ use defguard_common::db::{Id, models::Settings}; use ldap3::{Mod, SearchEntry}; use sqlx::{Error as SqlxError, PgExecutor}; -use super::{LDAPConfig, error::LdapError}; +use super::{ + LDAPConfig, + error::{LdapError, sanitize_ldap_string}, +}; use crate::{db::User, handlers::user::check_username, hashset}; pub(crate) enum UserObjectClass { @@ -69,12 +72,12 @@ impl User { if let Some(rdn) = extract_rdn_value(&entry.dn) { user.ldap_rdn = Some(rdn); } else { - return Err(LdapError::InvalidDN(entry.dn.clone())); + return Err(LdapError::InvalidDN(sanitize_ldap_string(&entry.dn))); } if let Some(dn_path) = extract_dn_path(&entry.dn) { user.ldap_user_path = Some(dn_path); } else { - return Err(LdapError::InvalidDN(entry.dn.clone())); + return Err(LdapError::InvalidDN(sanitize_ldap_string(&entry.dn))); } // Print the warning only if everything else checks out if check_username(username).is_err() { diff --git a/crates/defguard_event_logger/src/lib.rs b/crates/defguard_event_logger/src/lib.rs index 29c6123957..0c365ba15f 100644 --- a/crates/defguard_event_logger/src/lib.rs +++ b/crates/defguard_event_logger/src/lib.rs @@ -27,7 +27,7 @@ use message::{ DefguardEvent, EnrollmentEvent, EventContext, EventLoggerMessage, LoggerEvent, VpnEvent, }; use sqlx::PgPool; -use tokio::sync::mpsc::UnboundedReceiver; +use tokio::sync::{broadcast::Sender, mpsc::UnboundedReceiver}; use tracing::{debug, error, info, trace}; pub mod description; @@ -39,11 +39,12 @@ const MESSAGE_LIMIT: usize = 100; /// Run the event logger service /// /// This function runs in an infinite loop, receiving messages from the event_logger_rx channel -/// and storing them in the database as activity log events. +/// and storing them in the database as activity log events. Database errors are logged and the +/// loop restarts; the batch is discarded on error. pub async fn run_event_logger( pool: PgPool, mut event_logger_rx: UnboundedReceiver, - activity_log_messages_tx: tokio::sync::broadcast::Sender, + activity_log_messages_tx: Sender, ) -> Result<(), EventLoggerError> { info!("Starting activity log event logger service"); @@ -55,544 +56,550 @@ pub async fn run_event_logger( .recv_many(&mut message_buffer, MESSAGE_LIMIT) .await; + if message_count == 0 { + info!("Event logger channel closed, shutting down"); + return Ok(()); + } + debug!("Processing batch of {message_count} activity log events"); - let mut transaction = pool.begin().await?; - let mut serialized_activity_log_events = String::new(); + if let Err(e) = process_batch(&pool, message_buffer, &activity_log_messages_tx).await { + error!("Failed to process activity log event batch, batch will be discarded: {e}"); + continue; + } + } +} - // Process all messages in the batch - for message in message_buffer { - // Unpack shared event context - let EventContext { - user_id, - username, - location, - timestamp, - ip, - device, - } = message.context; +async fn process_batch( + pool: &PgPool, + message_buffer: Vec, + activity_log_messages_tx: &Sender, +) -> Result<(), EventLoggerError> { + let mut transaction = pool.begin().await?; + let mut serialized_activity_log_events = String::new(); - // Convert each message to a related activity log event - let activity_log_event = { - let (module, event, description, metadata) = match message.event { - LoggerEvent::Defguard(event) => { - let module = ActivityLogModule::Defguard; - let description = get_defguard_event_description(&event); + // Process all messages in the batch + for message in message_buffer { + // Unpack shared event context + let EventContext { + user_id, + username, + location, + timestamp, + ip, + device, + } = message.context; - let (event_type, metadata) = match *event { - DefguardEvent::UserLogin => (EventType::UserLogin, None), - DefguardEvent::UserLoginFailed { message } => ( - EventType::UserLoginFailed, - serde_json::to_value(LoginFailedMetadata { message }).ok(), - ), - DefguardEvent::UserMfaLogin { mfa_method } => ( - EventType::UserMfaLogin, - serde_json::to_value(MfaLoginMetadata { mfa_method }).ok(), - ), - DefguardEvent::UserMfaLoginFailed { + // Convert each message to a related activity log event + let activity_log_event = { + let (module, event, description, metadata) = match message.event { + LoggerEvent::Defguard(event) => { + let module = ActivityLogModule::Defguard; + let description = get_defguard_event_description(&event); + + let (event_type, metadata) = match *event { + DefguardEvent::UserLogin => (EventType::UserLogin, None), + DefguardEvent::UserLoginFailed { message } => ( + EventType::UserLoginFailed, + serde_json::to_value(LoginFailedMetadata { message }).ok(), + ), + DefguardEvent::UserMfaLogin { mfa_method } => ( + EventType::UserMfaLogin, + serde_json::to_value(MfaLoginMetadata { mfa_method }).ok(), + ), + DefguardEvent::UserMfaLoginFailed { + mfa_method, + message, + } => ( + EventType::UserMfaLoginFailed, + serde_json::to_value(MfaLoginFailedMetadata { mfa_method, message, - } => ( - EventType::UserMfaLoginFailed, - serde_json::to_value(MfaLoginFailedMetadata { - mfa_method, - message, - }) - .ok(), - ), - DefguardEvent::UserLogout => (EventType::UserLogout, None), - DefguardEvent::UserDeviceAdded { owner, device } => ( - EventType::DeviceAdded, - serde_json::to_value(DeviceMetadata { - owner: owner.into(), - device, - }) - .ok(), - ), - DefguardEvent::UserDeviceRemoved { owner, device } => ( - EventType::DeviceRemoved, - serde_json::to_value(DeviceMetadata { - owner: owner.into(), - device, - }) - .ok(), - ), - DefguardEvent::UserDeviceModified { - owner, + }) + .ok(), + ), + DefguardEvent::UserLogout => (EventType::UserLogout, None), + DefguardEvent::UserDeviceAdded { owner, device } => ( + EventType::DeviceAdded, + serde_json::to_value(DeviceMetadata { + owner: owner.into(), + device, + }) + .ok(), + ), + DefguardEvent::UserDeviceRemoved { owner, device } => ( + EventType::DeviceRemoved, + serde_json::to_value(DeviceMetadata { + owner: owner.into(), + device, + }) + .ok(), + ), + DefguardEvent::UserDeviceModified { + owner, + before, + after, + } => ( + EventType::DeviceModified, + serde_json::to_value(DeviceModifiedMetadata { + owner: owner.into(), before, after, - } => ( - EventType::DeviceModified, - serde_json::to_value(DeviceModifiedMetadata { - owner: owner.into(), - before, - after, - }) - .ok(), - ), - DefguardEvent::UserGroupsModified { - user, + }) + .ok(), + ), + DefguardEvent::UserGroupsModified { + user, + before, + after, + } => ( + EventType::UserGroupsModified, + serde_json::to_value(UserGroupsModifiedMetadata { + user: user.into(), before, after, - } => ( - EventType::UserGroupsModified, - serde_json::to_value(UserGroupsModifiedMetadata { - user: user.into(), - before, - after, - }) - .ok(), - ), - DefguardEvent::RecoveryCodeUsed => (EventType::RecoveryCodeUsed, None), - DefguardEvent::PasswordChanged => (EventType::PasswordChanged, None), - DefguardEvent::PasswordChangedByAdmin { user } => ( - EventType::PasswordChangedByAdmin, - serde_json::to_value(PasswordChangedByAdminMetadata { - user: user.into(), - }) - .ok(), - ), - DefguardEvent::MfaDisabled => (EventType::MfaDisabled, None), - DefguardEvent::UserMfaDisabled { user } => ( - EventType::UserMfaDisabled, - serde_json::to_value(UserMfaDisabledMetadata { user: user.into() }) - .ok(), - ), - DefguardEvent::MfaTotpEnabled => (EventType::MfaTotpEnabled, None), - DefguardEvent::MfaTotpDisabled => (EventType::MfaTotpDisabled, None), - DefguardEvent::MfaEmailEnabled => (EventType::MfaEmailEnabled, None), - DefguardEvent::MfaEmailDisabled => (EventType::MfaEmailDisabled, None), - DefguardEvent::MfaSecurityKeyAdded { key } => ( - EventType::MfaSecurityKeyAdded, - serde_json::to_value(MfaSecurityKeyMetadata { key: key.into() }) - .ok(), - ), - DefguardEvent::MfaSecurityKeyRemoved { key } => ( - EventType::MfaSecurityKeyRemoved, - serde_json::to_value(MfaSecurityKeyMetadata { key: key.into() }) - .ok(), - ), - DefguardEvent::AuthenticationKeyAdded { key } => ( - EventType::AuthenticationKeyAdded, - serde_json::to_value(AuthenticationKeyMetadata { key: key.into() }) - .ok(), - ), - DefguardEvent::AuthenticationKeyRemoved { key } => ( - EventType::AuthenticationKeyRemoved, - serde_json::to_value(AuthenticationKeyMetadata { key: key.into() }) - .ok(), - ), - DefguardEvent::AuthenticationKeyRenamed { - key, + }) + .ok(), + ), + DefguardEvent::RecoveryCodeUsed => (EventType::RecoveryCodeUsed, None), + DefguardEvent::PasswordChanged => (EventType::PasswordChanged, None), + DefguardEvent::PasswordChangedByAdmin { user } => ( + EventType::PasswordChangedByAdmin, + serde_json::to_value(PasswordChangedByAdminMetadata { + user: user.into(), + }) + .ok(), + ), + DefguardEvent::MfaDisabled => (EventType::MfaDisabled, None), + DefguardEvent::UserMfaDisabled { user } => ( + EventType::UserMfaDisabled, + serde_json::to_value(UserMfaDisabledMetadata { user: user.into() }) + .ok(), + ), + DefguardEvent::MfaTotpEnabled => (EventType::MfaTotpEnabled, None), + DefguardEvent::MfaTotpDisabled => (EventType::MfaTotpDisabled, None), + DefguardEvent::MfaEmailEnabled => (EventType::MfaEmailEnabled, None), + DefguardEvent::MfaEmailDisabled => (EventType::MfaEmailDisabled, None), + DefguardEvent::MfaSecurityKeyAdded { key } => ( + EventType::MfaSecurityKeyAdded, + serde_json::to_value(MfaSecurityKeyMetadata { key: key.into() }).ok(), + ), + DefguardEvent::MfaSecurityKeyRemoved { key } => ( + EventType::MfaSecurityKeyRemoved, + serde_json::to_value(MfaSecurityKeyMetadata { key: key.into() }).ok(), + ), + DefguardEvent::AuthenticationKeyAdded { key } => ( + EventType::AuthenticationKeyAdded, + serde_json::to_value(AuthenticationKeyMetadata { key: key.into() }) + .ok(), + ), + DefguardEvent::AuthenticationKeyRemoved { key } => ( + EventType::AuthenticationKeyRemoved, + serde_json::to_value(AuthenticationKeyMetadata { key: key.into() }) + .ok(), + ), + DefguardEvent::AuthenticationKeyRenamed { + key, + old_name, + new_name, + } => ( + EventType::AuthenticationKeyRenamed, + serde_json::to_value(AuthenticationKeyRenamedMetadata { + key: key.into(), old_name, new_name, - } => ( - EventType::AuthenticationKeyRenamed, - serde_json::to_value(AuthenticationKeyRenamedMetadata { - key: key.into(), - old_name, - new_name, - }) - .ok(), - ), - DefguardEvent::ApiTokenAdded { owner, token } => ( - EventType::ApiTokenAdded, - serde_json::to_value(ApiTokenMetadata { - owner: owner.into(), - token: token.into(), - }) - .ok(), - ), - DefguardEvent::ApiTokenRemoved { owner, token } => ( - EventType::ApiTokenRemoved, - serde_json::to_value(ApiTokenMetadata { - owner: owner.into(), - token: token.into(), - }) - .ok(), - ), - DefguardEvent::ApiTokenRenamed { - owner, - token, + }) + .ok(), + ), + DefguardEvent::ApiTokenAdded { owner, token } => ( + EventType::ApiTokenAdded, + serde_json::to_value(ApiTokenMetadata { + owner: owner.into(), + token: token.into(), + }) + .ok(), + ), + DefguardEvent::ApiTokenRemoved { owner, token } => ( + EventType::ApiTokenRemoved, + serde_json::to_value(ApiTokenMetadata { + owner: owner.into(), + token: token.into(), + }) + .ok(), + ), + DefguardEvent::ApiTokenRenamed { + owner, + token, + old_name, + new_name, + } => ( + EventType::ApiTokenRenamed, + serde_json::to_value(ApiTokenRenamedMetadata { + owner: owner.into(), + token: token.into(), old_name, new_name, - } => ( - EventType::ApiTokenRenamed, - serde_json::to_value(ApiTokenRenamedMetadata { - owner: owner.into(), - token: token.into(), - old_name, - new_name, - }) - .ok(), - ), - DefguardEvent::UserAdded { user } => ( - EventType::UserAdded, - serde_json::to_value(UserMetadata { user: user.into() }).ok(), - ), - DefguardEvent::UserRemoved { user } => ( - EventType::UserRemoved, - serde_json::to_value(UserMetadata { user: user.into() }).ok(), - ), - DefguardEvent::UserModified { before, after } => ( - EventType::UserModified, - serde_json::to_value(UserModifiedMetadata { - before: before.into(), - after: after.into(), - }) - .ok(), - ), - DefguardEvent::NetworkDeviceAdded { device, location } => ( - EventType::NetworkDeviceAdded, - serde_json::to_value(NetworkDeviceMetadata { device, location }) - .ok(), - ), - DefguardEvent::NetworkDeviceRemoved { device, location } => ( - EventType::NetworkDeviceRemoved, - serde_json::to_value(NetworkDeviceMetadata { device, location }) - .ok(), - ), - DefguardEvent::NetworkDeviceModified { - location, + }) + .ok(), + ), + DefguardEvent::UserAdded { user } => ( + EventType::UserAdded, + serde_json::to_value(UserMetadata { user: user.into() }).ok(), + ), + DefguardEvent::UserRemoved { user } => ( + EventType::UserRemoved, + serde_json::to_value(UserMetadata { user: user.into() }).ok(), + ), + DefguardEvent::UserModified { before, after } => ( + EventType::UserModified, + serde_json::to_value(UserModifiedMetadata { + before: before.into(), + after: after.into(), + }) + .ok(), + ), + DefguardEvent::NetworkDeviceAdded { device, location } => ( + EventType::NetworkDeviceAdded, + serde_json::to_value(NetworkDeviceMetadata { device, location }).ok(), + ), + DefguardEvent::NetworkDeviceRemoved { device, location } => ( + EventType::NetworkDeviceRemoved, + serde_json::to_value(NetworkDeviceMetadata { device, location }).ok(), + ), + DefguardEvent::NetworkDeviceModified { + location, + before, + after, + } => ( + EventType::NetworkDeviceModified, + serde_json::to_value(NetworkDeviceModifiedMetadata { before, after, - } => ( - EventType::NetworkDeviceModified, - serde_json::to_value(NetworkDeviceModifiedMetadata { - before, - after, - location, - }) - .ok(), - ), - DefguardEvent::VpnLocationAdded { location } => ( - EventType::VpnLocationAdded, - serde_json::to_value(VpnLocationMetadata { location }).ok(), - ), - DefguardEvent::VpnLocationRemoved { location } => ( - EventType::VpnLocationRemoved, - serde_json::to_value(VpnLocationMetadata { location }).ok(), - ), - DefguardEvent::VpnLocationModified { before, after } => ( - EventType::VpnLocationModified, - serde_json::to_value(VpnLocationModifiedMetadata { before, after }) - .ok(), - ), - DefguardEvent::OpenIdAppAdded { app } => ( - EventType::OpenIdAppAdded, - serde_json::to_value(OpenIdAppMetadata { app: app.into() }).ok(), - ), - DefguardEvent::OpenIdAppRemoved { app } => ( - EventType::OpenIdAppRemoved, - serde_json::to_value(OpenIdAppMetadata { app: app.into() }).ok(), - ), - DefguardEvent::OpenIdAppModified { before, after } => ( - EventType::OpenIdAppModified, - serde_json::to_value(OpenIdAppModifiedMetadata { - before: before.into(), - after: after.into(), - }) - .ok(), - ), - DefguardEvent::OpenIdAppStateChanged { app, enabled } => ( - EventType::OpenIdAppStateChanged, - serde_json::to_value(OpenIdAppStateChangedMetadata { - app: app.into(), - enabled, - }) - .ok(), - ), - DefguardEvent::OpenIdProviderModified { provider } => ( - EventType::OpenIdProviderModified, - serde_json::to_value(OpenIdProviderMetadata { - provider: provider.into(), - }) - .ok(), - ), - DefguardEvent::OpenIdProviderRemoved { provider } => ( - EventType::OpenIdProviderRemoved, - serde_json::to_value(OpenIdProviderMetadata { - provider: provider.into(), - }) - .ok(), - ), - DefguardEvent::SettingsUpdatedPartial { before, after } => ( - EventType::SettingsUpdatedPartial, - serde_json::to_value(SettingsUpdateMetadata { - before: before.into(), - after: after.into(), - }) - .ok(), - ), - DefguardEvent::SettingsUpdated { before, after } => ( - EventType::SettingsUpdated, - serde_json::to_value(SettingsUpdateMetadata { - before: before.into(), - after: after.into(), - }) - .ok(), - ), - DefguardEvent::SettingsDefaultBrandingRestored => { - (EventType::SettingsDefaultBrandingRestored, None) - } - DefguardEvent::ActivityLogStreamCreated { stream } => ( - EventType::ActivityLogStreamCreated, - serde_json::to_value(ActivityLogStreamMetadata { - stream: stream.into(), - }) - .ok(), - ), - DefguardEvent::ActivityLogStreamRemoved { stream } => ( - EventType::ActivityLogStreamRemoved, - serde_json::to_value(ActivityLogStreamMetadata { - stream: stream.into(), - }) - .ok(), - ), - DefguardEvent::ActivityLogStreamModified { before, after } => ( - EventType::ActivityLogStreamModified, - serde_json::to_value(ActivityLogStreamModifiedMetadata { - before: before.into(), - after: after.into(), - }) - .ok(), - ), - DefguardEvent::GroupsBulkAssigned { users, groups } => ( - EventType::GroupsBulkAssigned, - serde_json::to_value(GroupsBulkAssignedMetadata { - users: users.into_iter().map(Into::into).collect(), - groups, - }) - .ok(), - ), - DefguardEvent::GroupAdded { group } => ( - EventType::GroupAdded, - serde_json::to_value(GroupMetadata { group }).ok(), - ), - DefguardEvent::GroupModified { before, after } => ( - EventType::GroupModified, - serde_json::to_value(GroupModifiedMetadata { before, after }).ok(), - ), - DefguardEvent::GroupRemoved { group } => ( - EventType::GroupRemoved, - serde_json::to_value(GroupMetadata { group }).ok(), - ), - DefguardEvent::GroupMemberAdded { group, user } => ( - EventType::GroupMemberAdded, - serde_json::to_value(GroupAssignedMetadata { - group, - user: user.into(), - }) - .ok(), - ), - DefguardEvent::GroupMemberRemoved { group, user } => ( - EventType::GroupMemberRemoved, - serde_json::to_value(GroupAssignedMetadata { - group, - user: user.into(), - }) - .ok(), - ), - DefguardEvent::GroupMembersModified { + location, + }) + .ok(), + ), + DefguardEvent::VpnLocationAdded { location } => ( + EventType::VpnLocationAdded, + serde_json::to_value(VpnLocationMetadata { location }).ok(), + ), + DefguardEvent::VpnLocationRemoved { location } => ( + EventType::VpnLocationRemoved, + serde_json::to_value(VpnLocationMetadata { location }).ok(), + ), + DefguardEvent::VpnLocationModified { before, after } => ( + EventType::VpnLocationModified, + serde_json::to_value(VpnLocationModifiedMetadata { before, after }) + .ok(), + ), + DefguardEvent::OpenIdAppAdded { app } => ( + EventType::OpenIdAppAdded, + serde_json::to_value(OpenIdAppMetadata { app: app.into() }).ok(), + ), + DefguardEvent::OpenIdAppRemoved { app } => ( + EventType::OpenIdAppRemoved, + serde_json::to_value(OpenIdAppMetadata { app: app.into() }).ok(), + ), + DefguardEvent::OpenIdAppModified { before, after } => ( + EventType::OpenIdAppModified, + serde_json::to_value(OpenIdAppModifiedMetadata { + before: before.into(), + after: after.into(), + }) + .ok(), + ), + DefguardEvent::OpenIdAppStateChanged { app, enabled } => ( + EventType::OpenIdAppStateChanged, + serde_json::to_value(OpenIdAppStateChangedMetadata { + app: app.into(), + enabled, + }) + .ok(), + ), + DefguardEvent::OpenIdProviderModified { provider } => ( + EventType::OpenIdProviderModified, + serde_json::to_value(OpenIdProviderMetadata { + provider: provider.into(), + }) + .ok(), + ), + DefguardEvent::OpenIdProviderRemoved { provider } => ( + EventType::OpenIdProviderRemoved, + serde_json::to_value(OpenIdProviderMetadata { + provider: provider.into(), + }) + .ok(), + ), + DefguardEvent::SettingsUpdatedPartial { before, after } => ( + EventType::SettingsUpdatedPartial, + serde_json::to_value(SettingsUpdateMetadata { + before: before.into(), + after: after.into(), + }) + .ok(), + ), + DefguardEvent::SettingsUpdated { before, after } => ( + EventType::SettingsUpdated, + serde_json::to_value(SettingsUpdateMetadata { + before: before.into(), + after: after.into(), + }) + .ok(), + ), + DefguardEvent::SettingsDefaultBrandingRestored => { + (EventType::SettingsDefaultBrandingRestored, None) + } + DefguardEvent::ActivityLogStreamCreated { stream } => ( + EventType::ActivityLogStreamCreated, + serde_json::to_value(ActivityLogStreamMetadata { + stream: stream.into(), + }) + .ok(), + ), + DefguardEvent::ActivityLogStreamRemoved { stream } => ( + EventType::ActivityLogStreamRemoved, + serde_json::to_value(ActivityLogStreamMetadata { + stream: stream.into(), + }) + .ok(), + ), + DefguardEvent::ActivityLogStreamModified { before, after } => ( + EventType::ActivityLogStreamModified, + serde_json::to_value(ActivityLogStreamModifiedMetadata { + before: before.into(), + after: after.into(), + }) + .ok(), + ), + DefguardEvent::GroupsBulkAssigned { users, groups } => ( + EventType::GroupsBulkAssigned, + serde_json::to_value(GroupsBulkAssignedMetadata { + users: users.into_iter().map(Into::into).collect(), + groups, + }) + .ok(), + ), + DefguardEvent::GroupAdded { group } => ( + EventType::GroupAdded, + serde_json::to_value(GroupMetadata { group }).ok(), + ), + DefguardEvent::GroupModified { before, after } => ( + EventType::GroupModified, + serde_json::to_value(GroupModifiedMetadata { before, after }).ok(), + ), + DefguardEvent::GroupRemoved { group } => ( + EventType::GroupRemoved, + serde_json::to_value(GroupMetadata { group }).ok(), + ), + DefguardEvent::GroupMemberAdded { group, user } => ( + EventType::GroupMemberAdded, + serde_json::to_value(GroupAssignedMetadata { group, - added, - removed, - } => ( - EventType::GroupMembersModified, - serde_json::to_value(GroupMembersModifiedMetadata { - group, - added: added.into_iter().map(Into::into).collect(), - removed: removed.into_iter().map(Into::into).collect(), - }) - .ok(), - ), - DefguardEvent::WebHookAdded { webhook } => ( - EventType::WebHookAdded, - serde_json::to_value(WebHookMetadata { webhook }).ok(), - ), - DefguardEvent::WebHookModified { before, after } => ( - EventType::WebHookModified, - serde_json::to_value(WebHookModifiedMetadata { before, after }) - .ok(), - ), - DefguardEvent::WebHookRemoved { webhook } => ( - EventType::WebHookRemoved, - serde_json::to_value(WebHookMetadata { webhook }).ok(), - ), - DefguardEvent::WebHookStateChanged { webhook, enabled } => ( - EventType::WebHookStateChanged, - serde_json::to_value(WebHookStateChangedMetadata { - webhook, - enabled, - }) - .ok(), - ), - DefguardEvent::PasswordReset { user } => ( - EventType::PasswordReset, - serde_json::to_value(PasswordResetMetadata { user: user.into() }) - .ok(), - ), - DefguardEvent::ClientConfigurationTokenAdded { user } => ( - EventType::ClientConfigurationTokenAdded, - serde_json::to_value(ClientConfigurationTokenMetadata { - user: user.into(), - }) - .ok(), - ), - DefguardEvent::UserSnatBindingAdded { user, binding } => ( - EventType::UserSnatBindingAdded, - serde_json::to_value(UserSnatBindingMetadata { - user: user.into(), - binding, - }) - .ok(), - ), - DefguardEvent::UserSnatBindingRemoved { user, binding } => ( - EventType::UserSnatBindingRemoved, - serde_json::to_value(UserSnatBindingMetadata { - user: user.into(), - binding, - }) - .ok(), - ), - DefguardEvent::UserSnatBindingModified { - user, + user: user.into(), + }) + .ok(), + ), + DefguardEvent::GroupMemberRemoved { group, user } => ( + EventType::GroupMemberRemoved, + serde_json::to_value(GroupAssignedMetadata { + group, + user: user.into(), + }) + .ok(), + ), + DefguardEvent::GroupMembersModified { + group, + added, + removed, + } => ( + EventType::GroupMembersModified, + serde_json::to_value(GroupMembersModifiedMetadata { + group, + added: added.into_iter().map(Into::into).collect(), + removed: removed.into_iter().map(Into::into).collect(), + }) + .ok(), + ), + DefguardEvent::WebHookAdded { webhook } => ( + EventType::WebHookAdded, + serde_json::to_value(WebHookMetadata { webhook }).ok(), + ), + DefguardEvent::WebHookModified { before, after } => ( + EventType::WebHookModified, + serde_json::to_value(WebHookModifiedMetadata { before, after }).ok(), + ), + DefguardEvent::WebHookRemoved { webhook } => ( + EventType::WebHookRemoved, + serde_json::to_value(WebHookMetadata { webhook }).ok(), + ), + DefguardEvent::WebHookStateChanged { webhook, enabled } => ( + EventType::WebHookStateChanged, + serde_json::to_value(WebHookStateChangedMetadata { webhook, enabled }) + .ok(), + ), + DefguardEvent::PasswordReset { user } => ( + EventType::PasswordReset, + serde_json::to_value(PasswordResetMetadata { user: user.into() }).ok(), + ), + DefguardEvent::ClientConfigurationTokenAdded { user } => ( + EventType::ClientConfigurationTokenAdded, + serde_json::to_value(ClientConfigurationTokenMetadata { + user: user.into(), + }) + .ok(), + ), + DefguardEvent::UserSnatBindingAdded { user, binding } => ( + EventType::UserSnatBindingAdded, + serde_json::to_value(UserSnatBindingMetadata { + user: user.into(), + binding, + }) + .ok(), + ), + DefguardEvent::UserSnatBindingRemoved { user, binding } => ( + EventType::UserSnatBindingRemoved, + serde_json::to_value(UserSnatBindingMetadata { + user: user.into(), + binding, + }) + .ok(), + ), + DefguardEvent::UserSnatBindingModified { + user, + before, + after, + } => ( + EventType::UserSnatBindingModified, + serde_json::to_value(UserSnatBindingModifiedMetadata { + user: user.into(), before, after, - } => ( - EventType::UserSnatBindingModified, - serde_json::to_value(UserSnatBindingModifiedMetadata { - user: user.into(), - before, - after, - }) - .ok(), - ), - }; - (module, event_type, description, metadata) - } - LoggerEvent::Vpn(event) => { - let module = ActivityLogModule::Vpn; - let description = get_vpn_event_description(&event); + }) + .ok(), + ), + }; + (module, event_type, description, metadata) + } + LoggerEvent::Vpn(event) => { + let module = ActivityLogModule::Vpn; + let description = get_vpn_event_description(&event); - let (event_type, metadata) = match *event { - VpnEvent::MfaFailed { + let (event_type, metadata) = match *event { + VpnEvent::MfaFailed { + location, + device, + method, + message, + } => ( + EventType::VpnClientMfaFailed, + serde_json::to_value(VpnClientMfaFailedMetadata { location, device, method, message, - } => ( - EventType::VpnClientMfaFailed, - serde_json::to_value(VpnClientMfaFailedMetadata { - location, - device, - method, - message, - }) - .ok(), - ), - VpnEvent::ConnectedToMfaLocation { + }) + .ok(), + ), + VpnEvent::ConnectedToMfaLocation { + location, + device, + method, + } => ( + EventType::VpnClientConnectedMfa, + serde_json::to_value(VpnClientMfaMetadata { location, device, method, - } => ( - EventType::VpnClientConnectedMfa, - serde_json::to_value(VpnClientMfaMetadata { - location, - device, - method, - }) - .ok(), - ), - VpnEvent::DisconnectedFromMfaLocation { location, device } => ( - EventType::VpnClientDisconnectedMfa, - serde_json::to_value(VpnClientMetadata { location, device }).ok(), - ), - VpnEvent::ConnectedToLocation { location, device } => ( - EventType::VpnClientConnected, - serde_json::to_value(VpnClientMetadata { location, device }).ok(), - ), - VpnEvent::DisconnectedFromLocation { location, device } => ( - EventType::VpnClientDisconnected, - serde_json::to_value(VpnClientMetadata { location, device }).ok(), - ), - }; - (module, event_type, description, metadata) - } - LoggerEvent::Enrollment(event) => { - let module = ActivityLogModule::Enrollment; - let description = get_enrollment_event_description(&event); - - let (event_type, metadata) = match *event { - EnrollmentEvent::EnrollmentStarted => { - (EventType::EnrollmentStarted, None) - } - EnrollmentEvent::EnrollmentCompleted => { - (EventType::EnrollmentCompleted, None) - } - EnrollmentEvent::EnrollmentDeviceAdded { device } => ( - EventType::EnrollmentDeviceAdded, - serde_json::to_value(EnrollmentDeviceAddedMetadata { device }).ok(), - ), - EnrollmentEvent::PasswordResetRequested => { - (EventType::PasswordResetRequested, None) - } - EnrollmentEvent::PasswordResetStarted => { - (EventType::PasswordResetStarted, None) - } - EnrollmentEvent::PasswordResetCompleted => { - (EventType::PasswordResetCompleted, None) - } - EnrollmentEvent::TokenAdded { user } => ( - EventType::EnrollmentTokenAdded, - serde_json::to_value(EnrollmentTokenMetadata { user: user.into() }) - .ok(), - ), - }; - (module, event_type, description, metadata) - } - }; + }) + .ok(), + ), + VpnEvent::DisconnectedFromMfaLocation { location, device } => ( + EventType::VpnClientDisconnectedMfa, + serde_json::to_value(VpnClientMetadata { location, device }).ok(), + ), + VpnEvent::ConnectedToLocation { location, device } => ( + EventType::VpnClientConnected, + serde_json::to_value(VpnClientMetadata { location, device }).ok(), + ), + VpnEvent::DisconnectedFromLocation { location, device } => ( + EventType::VpnClientDisconnected, + serde_json::to_value(VpnClientMetadata { location, device }).ok(), + ), + }; + (module, event_type, description, metadata) + } + LoggerEvent::Enrollment(event) => { + let module = ActivityLogModule::Enrollment; + let description = get_enrollment_event_description(&event); - ActivityLogEvent { - id: NoId, - timestamp, - user_id, - username, - location, - ip: ip.into(), - event, - module, - device, - description, - metadata, + let (event_type, metadata) = match *event { + EnrollmentEvent::EnrollmentStarted => (EventType::EnrollmentStarted, None), + EnrollmentEvent::EnrollmentCompleted => { + (EventType::EnrollmentCompleted, None) + } + EnrollmentEvent::EnrollmentDeviceAdded { device } => ( + EventType::EnrollmentDeviceAdded, + serde_json::to_value(EnrollmentDeviceAddedMetadata { device }).ok(), + ), + EnrollmentEvent::PasswordResetRequested => { + (EventType::PasswordResetRequested, None) + } + EnrollmentEvent::PasswordResetStarted => { + (EventType::PasswordResetStarted, None) + } + EnrollmentEvent::PasswordResetCompleted => { + (EventType::PasswordResetCompleted, None) + } + EnrollmentEvent::TokenAdded { user } => ( + EventType::EnrollmentTokenAdded, + serde_json::to_value(EnrollmentTokenMetadata { user: user.into() }) + .ok(), + ), + }; + (module, event_type, description, metadata) } }; - match serde_json::to_string(&activity_log_event) { - Ok(serialized_activity_log_event) => { - serialized_activity_log_events += &(serialized_activity_log_event + "\n"); - } - Err(e) => { - error!("Failed to serialize activity log event. Reason: {e}"); - } + ActivityLogEvent { + id: NoId, + timestamp, + user_id, + username, + location, + ip: ip.into(), + event, + module, + device, + description, + metadata, } + }; - // Store activity log event in DB - // TODO: do batch inserts - activity_log_event.save(&mut *transaction).await?; - } - - // Send serialized events - if !serialized_activity_log_events.is_empty() { - let in_bytes = bytes::Bytes::from(serialized_activity_log_events); - if let Err(send_err) = activity_log_messages_tx.send(in_bytes) { - trace!( - "Sending serialized activity log events message failed. Most likely because there is no listeners. Reason: {send_err}" - ); + match serde_json::to_string(&activity_log_event) { + Ok(serialized_activity_log_event) => { + serialized_activity_log_events += &(serialized_activity_log_event + "\n"); + } + Err(e) => { + error!("Failed to serialize activity log event. Reason: {e}"); } } - // Commit the transaction - transaction.commit().await?; + // Store activity log event in DB + // TODO: do batch inserts + activity_log_event.save(&mut *transaction).await?; } + + // Send serialized events + if !serialized_activity_log_events.is_empty() { + let in_bytes = Bytes::from(serialized_activity_log_events); + if let Err(send_err) = activity_log_messages_tx.send(in_bytes) { + trace!( + "Sending serialized activity log events message failed. Most likely because there is no listeners. Reason: {send_err}" + ); + } + } + + // Commit the transaction + transaction.commit().await?; + Ok(()) } diff --git a/deny.toml b/deny.toml index 49991da9e5..f7872a9a66 100644 --- a/deny.toml +++ b/deny.toml @@ -72,6 +72,7 @@ feature-depth = 1 ignore = [ { id = "RUSTSEC-2023-0071", reason = "https://github.com/RustCrypto/RSA/issues/19" }, { id = "RUSTSEC-2024-0436", reason = "Unmaintained dependency of tera" }, + { id = "RUSTSEC-2026-0097", reason = "Unsound rand 0.8.5 advisory; silenced until jsonwebtoken upgrades their dependencies" }, ] # If this is true, then cargo deny will use the git executable to fetch advisory database. # If this is false, then it uses a built-in git library. diff --git a/e2e/package.json b/e2e/package.json index f7580c5acd..387f6592c6 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -32,9 +32,9 @@ "@scure/base": "^1.2.6", "@types/lodash": "^4.17.23", "@types/pg": "^8.16.0", - "axios": "^1.13.5", + "axios": "^1.15.0", "dotenv": "^17.3.1", - "lodash": "^4.17.23", + "lodash": "^4.18.1", "pg": "^8.18.0", "playwright": "^1.58.2", "totp-generator": "^1.0.0" diff --git a/e2e/pnpm-lock.yaml b/e2e/pnpm-lock.yaml index 1511514f90..3da4d10e59 100644 --- a/e2e/pnpm-lock.yaml +++ b/e2e/pnpm-lock.yaml @@ -24,14 +24,14 @@ importers: specifier: ^8.16.0 version: 8.16.0 axios: - specifier: ^1.13.5 - version: 1.13.5 + specifier: ^1.15.0 + version: 1.15.0 dotenv: specifier: ^17.3.1 version: 17.3.1 lodash: - specifier: ^4.17.23 - version: 4.17.23 + specifier: ^4.18.1 + version: 4.18.1 pg: specifier: ^8.18.0 version: 8.18.0 @@ -421,8 +421,8 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - axios@1.13.5: - resolution: {integrity: sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==} + axios@1.15.0: + resolution: {integrity: sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==} balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -947,8 +947,8 @@ packages: lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - lodash@4.17.23: - resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} @@ -1118,8 +1118,9 @@ packages: engines: {node: '>=14'} hasBin: true - proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + proxy-from-env@2.1.0: + resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} + engines: {node: '>=10'} punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} @@ -1672,11 +1673,11 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 - axios@1.13.5: + axios@1.15.0: dependencies: follow-redirects: 1.15.11 form-data: 4.0.5 - proxy-from-env: 1.1.0 + proxy-from-env: 2.1.0 transitivePeerDependencies: - debug @@ -2280,7 +2281,7 @@ snapshots: lodash.merge@4.6.2: {} - lodash@4.17.23: {} + lodash@4.18.1: {} math-intrinsics@1.1.0: {} @@ -2455,7 +2456,7 @@ snapshots: prettier@3.8.1: {} - proxy-from-env@1.1.0: {} + proxy-from-env@2.1.0: {} punycode@2.3.1: {} diff --git a/web/package.json b/web/package.json index 17b07d08ba..ed6cf2779d 100644 --- a/web/package.json +++ b/web/package.json @@ -61,7 +61,7 @@ "@tanstack/react-virtual": "3.13.12", "@tanstack/virtual-core": "3.13.12", "@use-gesture/react": "^10.3.1", - "axios": "^1.13.6", + "axios": "^1.15.0", "byte-size": "^9.0.1", "classnames": "^2.5.1", "clsx": "^2.1.1", @@ -81,7 +81,7 @@ "ipaddr.js": "^2.3.0", "itertools": "^2.6.0", "js-base64": "^3.7.8", - "lodash-es": "^4.17.23", + "lodash-es": "^4.18.1", "merge-refs": "^2.0.0", "millify": "^6.1.0", "motion": "^12.35.0", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index fa471353e5..2029d1b5f8 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -48,8 +48,8 @@ importers: specifier: ^10.3.1 version: 10.3.1(react@19.2.4) axios: - specifier: ^1.13.6 - version: 1.13.6 + specifier: ^1.15.0 + version: 1.15.0 byte-size: specifier: ^9.0.1 version: 9.0.1 @@ -108,8 +108,8 @@ importers: specifier: ^3.7.8 version: 3.7.8 lodash-es: - specifier: ^4.17.23 - version: 4.17.23 + specifier: ^4.18.1 + version: 4.18.1 merge-refs: specifier: ^2.0.0 version: 2.0.0(@types/react@19.2.14) @@ -402,24 +402,28 @@ packages: engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] + libc: [musl] '@biomejs/cli-linux-arm64@2.2.2': resolution: {integrity: sha512-JfrK3gdmWWTh2J5tq/rcWCOsImVyzUnOS2fkjhiYKCQ+v8PqM+du5cfB7G1kXas+7KQeKSWALv18iQqdtIMvzw==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] + libc: [glibc] '@biomejs/cli-linux-x64-musl@2.2.2': resolution: {integrity: sha512-ZCLXcZvjZKSiRY/cFANKg+z6Fhsf9MHOzj+NrDQcM+LbqYRT97LyCLWy2AS+W2vP+i89RyRM+kbGpUzbRTYWig==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] + libc: [musl] '@biomejs/cli-linux-x64@2.2.2': resolution: {integrity: sha512-Ogb+77edO5LEP/xbNicACOWVLt8mgC+E1wmpUakr+O4nKwLt9vXe74YNuT3T1dUBxC/SnrVmlzZFC7kQJEfquQ==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] + libc: [glibc] '@biomejs/cli-win32-arm64@2.2.2': resolution: {integrity: sha512-wBe2wItayw1zvtXysmHJQoQqXlTzHSpQRyPpJKiNIR21HzH/CrZRDFic1C1jDdp+zAPtqhNExa0owKMbNwW9cQ==} @@ -945,66 +949,79 @@ packages: resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.59.0': resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.59.0': resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.59.0': resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.59.0': resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-loong64-musl@4.59.0': resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} cpu: [loong64] os: [linux] + libc: [musl] '@rollup/rollup-linux-ppc64-gnu@4.59.0': resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-musl@4.59.0': resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} cpu: [ppc64] os: [linux] + libc: [musl] '@rollup/rollup-linux-riscv64-gnu@4.59.0': resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.59.0': resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.59.0': resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.59.0': resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.59.0': resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-openbsd-x64@4.59.0': resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} @@ -1094,24 +1111,28 @@ packages: engines: {node: '>=10'} cpu: [arm64] os: [linux] + libc: [glibc] '@swc/core-linux-arm64-musl@1.15.18': resolution: {integrity: sha512-0a+Lix+FSSHBSBOA0XznCcHo5/1nA6oLLjcnocvzXeqtdjnPb+SvchItHI+lfeiuj1sClYPDvPMLSLyXFaiIKw==} engines: {node: '>=10'} cpu: [arm64] os: [linux] + libc: [musl] '@swc/core-linux-x64-gnu@1.15.18': resolution: {integrity: sha512-wG9J8vReUlpaHz4KOD/5UE1AUgirimU4UFT9oZmupUDEofxJKYb1mTA/DrMj0s78bkBiNI+7Fo2EgPuvOJfuAA==} engines: {node: '>=10'} cpu: [x64] os: [linux] + libc: [glibc] '@swc/core-linux-x64-musl@1.15.18': resolution: {integrity: sha512-4nwbVvCphKzicwNWRmvD5iBaZj8JYsRGa4xOxJmOyHlMDpsvvJ2OR2cODlvWyGFH6BYL1MfIAK3qph3hp0Az6g==} engines: {node: '>=10'} cpu: [x64] os: [linux] + libc: [musl] '@swc/core-win32-arm64-msvc@1.15.18': resolution: {integrity: sha512-zk0RYO+LjiBCat2RTMHzAWaMky0cra9loH4oRrLKLLNuL+jarxKLFDA8xTZWEkCPLjUTwlRN7d28eDLLMgtUcQ==} @@ -1379,8 +1400,8 @@ packages: peerDependencies: postcss: ^8.1.0 - axios@1.13.6: - resolution: {integrity: sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==} + axios@1.15.0: + resolution: {integrity: sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==} babel-plugin-macros@3.1.0: resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} @@ -2256,8 +2277,8 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} - lodash-es@4.17.23: - resolution: {integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==} + lodash-es@4.18.1: + resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==} lodash.ismatch@4.4.0: resolution: {integrity: sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==} @@ -2612,8 +2633,9 @@ packages: proxy-compare@3.0.1: resolution: {integrity: sha512-V9plBAt3qjMlS1+nC8771KNf6oJ12gExvaxnNzN/9yVRLdTv/lc+oJlnSzrdYDAvBfTStPCoiaCOTmTs0adv7Q==} - proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + proxy-from-env@2.1.0: + resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} + engines: {node: '>=10'} q@1.5.1: resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} @@ -4315,11 +4337,11 @@ snapshots: postcss: 8.5.8 postcss-value-parser: 4.2.0 - axios@1.13.6: + axios@1.15.0: dependencies: follow-redirects: 1.15.11 form-data: 4.0.5 - proxy-from-env: 1.1.0 + proxy-from-env: 2.1.0 transitivePeerDependencies: - debug @@ -5244,7 +5266,7 @@ snapshots: dependencies: p-locate: 5.0.0 - lodash-es@4.17.23: {} + lodash-es@4.18.1: {} lodash.ismatch@4.4.0: {} @@ -5703,7 +5725,7 @@ snapshots: proxy-compare@3.0.1: {} - proxy-from-env@1.1.0: {} + proxy-from-env@2.1.0: {} q@1.5.1: {}