mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2025-07-05 19:55:48 +02:00
Compare commits
4 commits
2ccbd7d60b
...
2e0bb01595
Author | SHA1 | Date | |
---|---|---|---|
|
2e0bb01595 | ||
|
d9d8e0acfd | ||
|
e8d823a653 | ||
|
0ba77674c7 |
5 changed files with 175 additions and 1 deletions
63
SECURITY.md
Normal file
63
SECURITY.md
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
# 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 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** directly 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 branch
|
||||||
|
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.
|
|
@ -20,3 +20,4 @@
|
||||||
- [Testing](development/testing.md)
|
- [Testing](development/testing.md)
|
||||||
- [Hot Reloading ("Live" Development)](development/hot_reload.md)
|
- [Hot Reloading ("Live" Development)](development/hot_reload.md)
|
||||||
- [Community (and Guidelines)](community.md)
|
- [Community (and Guidelines)](community.md)
|
||||||
|
- [Security](security.md)
|
||||||
|
|
1
docs/security.md
Normal file
1
docs/security.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{{#include ../SECURITY.md}}
|
|
@ -2162,6 +2162,109 @@ 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
|
let server_in_room = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
|
@ -2209,6 +2312,12 @@ async fn knock_room_helper_local(
|
||||||
return Err!(Request(Forbidden("This room does not support knocking.")));
|
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 {
|
let content = RoomMemberEventContent {
|
||||||
displayname: services.users.displayname(sender_user).await.ok(),
|
displayname: services.users.displayname(sender_user).await.ok(),
|
||||||
avatar_url: services.users.avatar_url(sender_user).await.ok(),
|
avatar_url: services.users.avatar_url(sender_user).await.ok(),
|
||||||
|
|
|
@ -638,7 +638,7 @@ fn valid_membership_change(
|
||||||
warn!(?target_user_membership_event_id, "Banned user can't join");
|
warn!(?target_user_membership_event_id, "Banned user can't join");
|
||||||
false
|
false
|
||||||
} else if (join_rules == JoinRule::Invite
|
} else if (join_rules == JoinRule::Invite
|
||||||
|| room_version.allow_knocking && join_rules == JoinRule::Knock)
|
|| room_version.allow_knocking && (join_rules == JoinRule::Knock || matches!(join_rules, JoinRule::KnockRestricted(_))))
|
||||||
// If the join_rule is invite then allow if membership state is invite or join
|
// 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::Join
|
||||||
|| target_user_current_membership == MembershipState::Invite)
|
|| target_user_current_membership == MembershipState::Invite)
|
||||||
|
|
Loading…
Add table
Reference in a new issue