diff --git a/.forgejo/actions/rust-toolchain/action.yml b/.forgejo/actions/rust-toolchain/action.yml index ae5cfcee..71fb96f5 100644 --- a/.forgejo/actions/rust-toolchain/action.yml +++ b/.forgejo/actions/rust-toolchain/action.yml @@ -19,20 +19,11 @@ outputs: rustc_version: description: The rustc version installed value: ${{ steps.rustc-version.outputs.version }} - rustup_version: - description: The rustup version installed - value: ${{ steps.rustup-version.outputs.version }} runs: using: composite steps: - - name: Check if rustup is already installed - shell: bash - id: rustup-version - run: | - echo "version=$(rustup --version)" >> $GITHUB_OUTPUT - name: Cache rustup toolchains - if: steps.rustup-version.outputs.version == '' uses: actions/cache@v3 with: path: | @@ -42,7 +33,6 @@ runs: # Requires repo to be cloned if toolchain is not specified key: ${{ runner.os }}-rustup-${{ inputs.toolchain || hashFiles('**/rust-toolchain.toml') }} - name: Install Rust toolchain - if: steps.rustup-version.outputs.version == '' shell: bash run: | if ! command -v rustup &> /dev/null ; then diff --git a/.forgejo/workflows/release-image.yml b/.forgejo/workflows/release-image.yml index 55b303b2..ec466c58 100644 --- a/.forgejo/workflows/release-image.yml +++ b/.forgejo/workflows/release-image.yml @@ -57,6 +57,7 @@ jobs: build-image: runs-on: dind + container: ghcr.io/catthehacker/ubuntu:act-latest needs: define-variables permissions: contents: read @@ -180,14 +181,14 @@ jobs: file: "docker/Dockerfile" build-args: | GIT_COMMIT_HASH=${{ github.sha }}) - GIT_COMMIT_HASH_SHORT=${{ env.COMMIT_SHORT_SHA }} + GIT_COMMIT_HASH_SHORT=${{ env.COMMIT_SHORT_SHA }}) GIT_REMOTE_URL=${{github.event.repository.html_url }} GIT_REMOTE_COMMIT_URL=${{github.event.head_commit.url }} platforms: ${{ matrix.platform }} labels: ${{ steps.meta.outputs.labels }} annotations: ${{ steps.meta.outputs.annotations }} cache-from: type=gha - # cache-to: type=gha,mode=max + cache-to: type=gha,mode=max sbom: true outputs: type=image,"name=${{ needs.define-variables.outputs.images_list }}",push-by-digest=true,name-canonical=true,push=true env: @@ -210,6 +211,7 @@ jobs: merge: runs-on: dind + container: ghcr.io/catthehacker/ubuntu:act-latest needs: [define-variables, build-image] steps: - name: Download digests diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index 2869ce58..00000000 --- a/SECURITY.md +++ /dev/null @@ -1,63 +0,0 @@ -# Security Policy for Continuwuity - -This document outlines the security policy for Continuwuity. Our goal is to maintain a secure platform for all users, and we take security matters seriously. - -## Supported Versions - -We provide security updates for the following versions of Continuwuity: - -| Version | Supported | -| -------------- |:----------------:| -| Latest release | ✅ | -| Main branch | ✅ | -| Older releases | ❌ | - -We may backport fixes to the previous release at our discretion, but we don't guarantee this. - -## Reporting a Vulnerability - -### Responsible Disclosure - -We appreciate the efforts of security researchers and the community in identifying and reporting vulnerabilities. To ensure that potential vulnerabilities are addressed properly, please follow these guidelines: - -1. **Contact members of the team directly** over E2EE private message. - - [@jade:ellis.link](https://matrix.to/#/@jade:ellis.link) - - [@nex:nexy7574.co.uk](https://matrix.to/#/@nex:nexy7574.co.uk) -2. **Email the security team** at [security@continuwuity.org](mailto:security@continuwuity.org). This is not E2EE, so don't include sensitive details. -3. **Do not disclose the vulnerability publicly** until it has been addressed -4. **Provide detailed information** about the vulnerability, including: - - A clear description of the issue - - Steps to reproduce - - Potential impact - - Any possible mitigations - - Version(s) affected, including specific commits if possible - -If you have any doubts about a potential security vulnerability, contact us via private channels first! We'd prefer that you bother us, instead of having a vulnerability disclosed without a fix. - -### What to Expect - -When you report a security vulnerability: - -1. **Acknowledgment**: We will acknowledge receipt of your report. -2. **Assessment**: We will assess the vulnerability and determine its impact on our users -3. **Updates**: We will provide updates on our progress in addressing the vulnerability, and may request you help test mitigations -4. **Resolution**: Once resolved, we will notify you and discuss coordinated disclosure -5. **Credit**: We will recognize your contribution (unless you prefer to remain anonymous) - -## Security Update Process - -When security vulnerabilities are identified: - -1. We will develop and test fixes in a private fork -2. Security updates will be released as soon as possible -3. Release notes will include information about the vulnerabilities, avoiding details that could facilitate exploitation where possible -4. Critical security updates may be backported to the previous stable release - -## Additional Resources - -- [Matrix Security Disclosure Policy](https://matrix.org/security-disclosure-policy/) -- [Continuwuity Documentation](https://continuwuity.org/introduction) - ---- - -This security policy was last updated on May 25, 2025. diff --git a/conduwuit-example.toml b/conduwuit-example.toml index 1a8be2aa..6934e67c 100644 --- a/conduwuit-example.toml +++ b/conduwuit-example.toml @@ -1641,29 +1641,19 @@ # #server = -# URL to a support page for the server, which will be served as part of -# the MSC1929 server support endpoint at /.well-known/matrix/support. -# Will be included alongside any contact information +# This item is undocumented. Please contribute documentation for it. # #support_page = -# Role string for server support contacts, to be served as part of the -# MSC1929 server support endpoint at /.well-known/matrix/support. +# This item is undocumented. Please contribute documentation for it. # -#support_role = "m.role.admin" +#support_role = -# Email address for server support contacts, to be served as part of the -# MSC1929 server support endpoint. -# This will be used along with support_mxid if specified. +# This item is undocumented. Please contribute documentation for it. # #support_email = -# Matrix ID for server support contacts, to be served as part of the -# MSC1929 server support endpoint. -# This will be used along with support_email if specified. -# -# If no email or mxid is specified, all of the server's admins will be -# listed. +# This item is undocumented. Please contribute documentation for it. # #support_mxid = diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index af729003..473c9e74 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -20,4 +20,3 @@ - [Testing](development/testing.md) - [Hot Reloading ("Live" Development)](development/hot_reload.md) - [Community (and Guidelines)](community.md) -- [Security](security.md) diff --git a/docs/security.md b/docs/security.md deleted file mode 100644 index b4474cf5..00000000 --- a/docs/security.md +++ /dev/null @@ -1 +0,0 @@ -{{#include ../SECURITY.md}} diff --git a/src/admin/debug/mod.rs b/src/admin/debug/mod.rs index 1fd4e263..9b86f18c 100644 --- a/src/admin/debug/mod.rs +++ b/src/admin/debug/mod.rs @@ -125,13 +125,13 @@ pub(super) enum DebugCommand { reset: bool, }, - /// - Sign JSON blob + /// - Verify json signatures /// /// This command needs a JSON blob provided in a Markdown code block below /// the command. SignJson, - /// - Verify JSON signatures + /// - Verify json signatures /// /// This command needs a JSON blob provided in a Markdown code block below /// the command. diff --git a/src/api/client/membership.rs b/src/api/client/membership.rs index e587d806..2847d668 100644 --- a/src/api/client/membership.rs +++ b/src/api/client/membership.rs @@ -2162,109 +2162,6 @@ async fn knock_room_by_id_helper( } } - // For knock_restricted rooms, check if the user meets the restricted conditions - // If they do, attempt to join instead of knock - // This is not mentioned in the spec, but should be allowable (we're allowed to - // auto-join invites to knocked rooms) - let join_rule = services.rooms.state_accessor.get_join_rules(room_id).await; - if let JoinRule::KnockRestricted(restricted) = &join_rule { - let restriction_rooms: Vec<_> = restricted - .allow - .iter() - .filter_map(|a| match a { - | AllowRule::RoomMembership(r) => Some(&r.room_id), - | _ => None, - }) - .collect(); - - // Check if the user is in any of the allowed rooms - let mut user_meets_restrictions = false; - for restriction_room_id in &restriction_rooms { - if services - .rooms - .state_cache - .is_joined(sender_user, restriction_room_id) - .await - { - user_meets_restrictions = true; - break; - } - } - - // If the user meets the restrictions, try joining instead - if user_meets_restrictions { - debug_info!( - "{sender_user} meets the restricted criteria in knock_restricted room \ - {room_id}, attempting to join instead of knock" - ); - // For this case, we need to drop the state lock and get a new one in - // join_room_by_id_helper We need to release the lock here and let - // join_room_by_id_helper acquire it again - drop(state_lock); - match join_room_by_id_helper( - services, - sender_user, - room_id, - reason.clone(), - servers, - None, - &None, - ) - .await - { - | Ok(_) => return Ok(knock_room::v3::Response::new(room_id.to_owned())), - | Err(e) => { - debug_warn!( - "Failed to convert knock to join for {sender_user} in {room_id}: {e:?}" - ); - // Get a new state lock for the remaining knock logic - let new_state_lock = services.rooms.state.mutex.lock(room_id).await; - - let server_in_room = services - .rooms - .state_cache - .server_in_room(services.globals.server_name(), room_id) - .await; - - let local_knock = server_in_room - || servers.is_empty() - || (servers.len() == 1 && services.globals.server_is_ours(&servers[0])); - - if local_knock { - knock_room_helper_local( - services, - sender_user, - room_id, - reason, - servers, - new_state_lock, - ) - .boxed() - .await?; - } else { - knock_room_helper_remote( - services, - sender_user, - room_id, - reason, - servers, - new_state_lock, - ) - .boxed() - .await?; - } - - return Ok(knock_room::v3::Response::new(room_id.to_owned())); - }, - } - } - } else if !matches!(join_rule, JoinRule::Knock | JoinRule::KnockRestricted(_)) { - debug_warn!( - "{sender_user} attempted to knock on room {room_id} but its join rule is \ - {join_rule:?}, not knock or knock_restricted" - ); - } - let server_in_room = services .rooms .state_cache @@ -2312,12 +2209,6 @@ async fn knock_room_helper_local( return Err!(Request(Forbidden("This room does not support knocking."))); } - // Verify that this room has a valid knock or knock_restricted join rule - let join_rule = services.rooms.state_accessor.get_join_rules(room_id).await; - if !matches!(join_rule, JoinRule::Knock | JoinRule::KnockRestricted(_)) { - return Err!(Request(Forbidden("This room's join rule does not allow knocking."))); - } - let content = RoomMemberEventContent { displayname: services.users.displayname(sender_user).await.ok(), avatar_url: services.users.avatar_url(sender_user).await.ok(), diff --git a/src/api/client/well_known.rs b/src/api/client/well_known.rs index fe2281ba..35b7fc1e 100644 --- a/src/api/client/well_known.rs +++ b/src/api/client/well_known.rs @@ -1,6 +1,5 @@ use axum::{Json, extract::State, response::IntoResponse}; use conduwuit::{Error, Result}; -use futures::StreamExt; use ruma::api::client::{ discovery::{ discover_homeserver::{self, HomeserverInfo, SlidingSyncProxyInfo}, @@ -18,7 +17,7 @@ pub(crate) async fn well_known_client( State(services): State, _body: Ruma, ) -> Result { - let client_url = match services.config.well_known.client.as_ref() { + let client_url = match services.server.config.well_known.client.as_ref() { | Some(url) => url.to_string(), | None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")), }; @@ -34,63 +33,44 @@ pub(crate) async fn well_known_client( /// # `GET /.well-known/matrix/support` /// /// Server support contact and support page of a homeserver's domain. -/// Implements MSC1929 for server discovery. -/// If no configuration is set, uses admin users as contacts. pub(crate) async fn well_known_support( State(services): State, _body: Ruma, ) -> Result { let support_page = services + .server .config .well_known .support_page .as_ref() .map(ToString::to_string); - let email_address = services.config.well_known.support_email.clone(); - let matrix_id = services.config.well_known.support_mxid.clone(); + let role = services.server.config.well_known.support_role.clone(); + + // support page or role must be either defined for this to be valid + if support_page.is_none() && role.is_none() { + return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")); + } + + let email_address = services.server.config.well_known.support_email.clone(); + let matrix_id = services.server.config.well_known.support_mxid.clone(); + + // if a role is specified, an email address or matrix id is required + if role.is_some() && (email_address.is_none() && matrix_id.is_none()) { + return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")); + } // TODO: support defining multiple contacts in the config let mut contacts: Vec = vec![]; - let role_value = services - .config - .well_known - .support_role - .clone() - .unwrap_or_else(|| "m.role.admin".to_owned().into()); + if let Some(role) = role { + let contact = Contact { role, email_address, matrix_id }; - // Add configured contact if at least one contact method is specified - if email_address.is_some() || matrix_id.is_some() { - contacts.push(Contact { - role: role_value.clone(), - email_address: email_address.clone(), - matrix_id: matrix_id.clone(), - }); - } - - // Try to add admin users as contacts if no contacts are configured - if contacts.is_empty() { - if let Ok(admin_room) = services.admin.get_admin_room().await { - let admin_users = services.rooms.state_cache.room_members(&admin_room); - let mut stream = admin_users; - - while let Some(user_id) = stream.next().await { - // Skip server user - if *user_id == services.globals.server_user { - break; - } - contacts.push(Contact { - role: role_value.clone(), - email_address: None, - matrix_id: Some(user_id.to_owned()), - }); - } - } + contacts.push(contact); } + // support page or role+contacts must be either defined for this to be valid if contacts.is_empty() && support_page.is_none() { - // No admin room, no configured contacts, and no support page return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")); } @@ -104,9 +84,9 @@ pub(crate) async fn well_known_support( pub(crate) async fn syncv3_client_server_json( State(services): State, ) -> Result { - let server_url = match services.config.well_known.client.as_ref() { + let server_url = match services.server.config.well_known.client.as_ref() { | Some(url) => url.to_string(), - | None => match services.config.well_known.server.as_ref() { + | None => match services.server.config.well_known.server.as_ref() { | Some(url) => url.to_string(), | None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")), }, diff --git a/src/build_metadata/build.rs b/src/build_metadata/build.rs index bf84d508..bfdf20b1 100644 --- a/src/build_metadata/build.rs +++ b/src/build_metadata/build.rs @@ -79,12 +79,12 @@ fn main() { // --- Rerun Triggers --- // TODO: The git rerun triggers seem to always run - // // Rerun if the git HEAD changes - // println!("cargo:rerun-if-changed=.git/HEAD"); - // // Rerun if the ref pointed to by HEAD changes (e.g., new commit on branch) - // if let Some(ref_path) = run_git_command(&["symbolic-ref", "--quiet", "HEAD"]) - // { println!("cargo:rerun-if-changed=.git/{ref_path}"); - // } + // Rerun if the git HEAD changes + println!("cargo:rerun-if-changed=.git/HEAD"); + // Rerun if the ref pointed to by HEAD changes (e.g., new commit on branch) + if let Some(ref_path) = run_git_command(&["symbolic-ref", "--quiet", "HEAD"]) { + println!("cargo:rerun-if-changed=.git/{ref_path}"); + } println!("cargo:rerun-if-env-changed=GIT_COMMIT_HASH"); println!("cargo:rerun-if-env-changed=GIT_COMMIT_HASH_SHORT"); diff --git a/src/core/config/check.rs b/src/core/config/check.rs index 3dc45e2f..ded9533d 100644 --- a/src/core/config/check.rs +++ b/src/core/config/check.rs @@ -219,15 +219,6 @@ pub fn check(config: &Config) -> Result { )); } - // Check if support contact information is configured - if config.well_known.support_email.is_none() && config.well_known.support_mxid.is_none() { - warn!( - "No support contact information (support_email or support_mxid) is configured in \ - the well_known section. Users in the admin room will be automatically listed as \ - support contacts in the /.well-known/matrix/support endpoint." - ); - } - if config .url_preview_domain_contains_allowlist .contains(&"*".to_owned()) diff --git a/src/core/config/mod.rs b/src/core/config/mod.rs index d4a10345..66ed0b2e 100644 --- a/src/core/config/mod.rs +++ b/src/core/config/mod.rs @@ -1897,28 +1897,12 @@ pub struct WellKnownConfig { /// example: "matrix.example.com:443" pub server: Option, - /// URL to a support page for the server, which will be served as part of - /// the MSC1929 server support endpoint at /.well-known/matrix/support. - /// Will be included alongside any contact information pub support_page: Option, - /// Role string for server support contacts, to be served as part of the - /// MSC1929 server support endpoint at /.well-known/matrix/support. - /// - /// default: "m.role.admin" pub support_role: Option, - /// Email address for server support contacts, to be served as part of the - /// MSC1929 server support endpoint. - /// This will be used along with support_mxid if specified. pub support_email: Option, - /// Matrix ID for server support contacts, to be served as part of the - /// MSC1929 server support endpoint. - /// This will be used along with support_email if specified. - /// - /// If no email or mxid is specified, all of the server's admins will be - /// listed. pub support_mxid: Option, } diff --git a/src/core/matrix/state_res/event_auth.rs b/src/core/matrix/state_res/event_auth.rs index 759ab5cb..715e5156 100644 --- a/src/core/matrix/state_res/event_auth.rs +++ b/src/core/matrix/state_res/event_auth.rs @@ -638,7 +638,7 @@ fn valid_membership_change( warn!(?target_user_membership_event_id, "Banned user can't join"); false } else if (join_rules == JoinRule::Invite - || room_version.allow_knocking && (join_rules == JoinRule::Knock || matches!(join_rules, JoinRule::KnockRestricted(_)))) + || room_version.allow_knocking && join_rules == JoinRule::Knock) // If the join_rule is invite then allow if membership state is invite or join && (target_user_current_membership == MembershipState::Join || target_user_current_membership == MembershipState::Invite) diff --git a/src/core/mod.rs b/src/core/mod.rs index aaacd4d8..b91cdf0b 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -21,10 +21,7 @@ pub use ::toml; pub use ::tracing; pub use config::Config; pub use error::Error; -pub use info::{ - rustc_flags_capture, version, - version::{name, version}, -}; +pub use info::{rustc_flags_capture, version, version::version}; pub use matrix::{Event, EventTypeExt, PduCount, PduEvent, PduId, RoomVersion, pdu, state_res}; pub use server::Server; pub use utils::{ctor, dtor, implement, result, result::Result}; diff --git a/src/main/clap.rs b/src/main/clap.rs index a3b2b19a..9b63af19 100644 --- a/src/main/clap.rs +++ b/src/main/clap.rs @@ -15,7 +15,7 @@ use conduwuit_core::{ #[clap( about, long_about = None, - name = conduwuit_core::name(), + name = "conduwuit", version = conduwuit_core::version(), )] pub(crate) struct Args {