mirror of
https://github.com/dani-garcia/vaultwarden.git
synced 2025-08-31 23:14:48 +00:00
Compare commits
No commits in common. "175d488d6ccf62617170107c6d38f90f28e070c2" and "004aae9daa1692d727db99e2aecb909387848197" have entirely different histories.
175d488d6c
...
004aae9daa
10 changed files with 25 additions and 115 deletions
|
@ -403,7 +403,7 @@ async fn delete_user(user_id: UserId, token: AdminToken, mut conn: DbConn) -> Em
|
||||||
|
|
||||||
for membership in memberships {
|
for membership in memberships {
|
||||||
log_event(
|
log_event(
|
||||||
EventType::OrganizationUserDeleted as i32,
|
EventType::OrganizationUserRemoved as i32,
|
||||||
&membership.uuid,
|
&membership.uuid,
|
||||||
&membership.org_uuid,
|
&membership.org_uuid,
|
||||||
&ACTING_ADMIN_USER.into(),
|
&ACTING_ADMIN_USER.into(),
|
||||||
|
|
|
@ -303,11 +303,7 @@ async fn post_set_password(data: Json<SetPasswordData>, headers: Headers, mut co
|
||||||
Membership::accept_user_invitations(&user.uuid, &mut conn).await?;
|
Membership::accept_user_invitations(&user.uuid, &mut conn).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_user_event(EventType::UserChangedPassword as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
user.save(&mut conn).await?;
|
user.save(&mut conn).await?;
|
||||||
|
|
||||||
Ok(Json(json!({
|
Ok(Json(json!({
|
||||||
"Object": "set-password",
|
"Object": "set-password",
|
||||||
"CaptchaBypassToken": "",
|
"CaptchaBypassToken": "",
|
||||||
|
@ -1299,15 +1295,6 @@ async fn post_auth_request(
|
||||||
|
|
||||||
nt.send_auth_request(&user.uuid, &auth_request.uuid, &data.device_identifier, &mut conn).await;
|
nt.send_auth_request(&user.uuid, &auth_request.uuid, &data.device_identifier, &mut conn).await;
|
||||||
|
|
||||||
log_user_event(
|
|
||||||
EventType::UserRequestedDeviceApproval as i32,
|
|
||||||
&user.uuid,
|
|
||||||
client_headers.device_type,
|
|
||||||
&client_headers.ip.ip,
|
|
||||||
&mut conn,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
Ok(Json(json!({
|
Ok(Json(json!({
|
||||||
"id": auth_request.uuid,
|
"id": auth_request.uuid,
|
||||||
"publicKey": auth_request.public_key,
|
"publicKey": auth_request.public_key,
|
||||||
|
@ -1389,26 +1376,9 @@ async fn put_auth_request(
|
||||||
|
|
||||||
ant.send_auth_response(&auth_request.user_uuid, &auth_request.uuid).await;
|
ant.send_auth_response(&auth_request.user_uuid, &auth_request.uuid).await;
|
||||||
nt.send_auth_response(&auth_request.user_uuid, &auth_request.uuid, &data.device_identifier, &mut conn).await;
|
nt.send_auth_response(&auth_request.user_uuid, &auth_request.uuid, &data.device_identifier, &mut conn).await;
|
||||||
|
|
||||||
log_user_event(
|
|
||||||
EventType::OrganizationUserApprovedAuthRequest as i32,
|
|
||||||
&headers.user.uuid,
|
|
||||||
headers.device.atype,
|
|
||||||
&headers.ip.ip,
|
|
||||||
&mut conn,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
} else {
|
} else {
|
||||||
// If denied, there's no reason to keep the request
|
// If denied, there's no reason to keep the request
|
||||||
auth_request.delete(&mut conn).await?;
|
auth_request.delete(&mut conn).await?;
|
||||||
log_user_event(
|
|
||||||
EventType::OrganizationUserRejectedAuthRequest as i32,
|
|
||||||
&headers.user.uuid,
|
|
||||||
headers.device.atype,
|
|
||||||
&headers.ip.ip,
|
|
||||||
&mut conn,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Json(json!({
|
Ok(Json(json!({
|
||||||
|
|
|
@ -245,8 +245,8 @@ async fn _log_user_event(
|
||||||
ip: &IpAddr,
|
ip: &IpAddr,
|
||||||
conn: &mut DbConn,
|
conn: &mut DbConn,
|
||||||
) {
|
) {
|
||||||
let memberships = Membership::find_by_user(user_id, conn).await;
|
let orgs = Membership::get_orgs_by_user(user_id, conn).await;
|
||||||
let mut events: Vec<Event> = Vec::with_capacity(memberships.len() + 1); // We need an event per org and one without an org
|
let mut events: Vec<Event> = Vec::with_capacity(orgs.len() + 1); // We need an event per org and one without an org
|
||||||
|
|
||||||
// Upstream saves the event also without any org_id.
|
// Upstream saves the event also without any org_id.
|
||||||
let mut event = Event::new(event_type, event_date);
|
let mut event = Event::new(event_type, event_date);
|
||||||
|
@ -257,11 +257,10 @@ async fn _log_user_event(
|
||||||
events.push(event);
|
events.push(event);
|
||||||
|
|
||||||
// For each org a user is a member of store these events per org
|
// For each org a user is a member of store these events per org
|
||||||
for membership in memberships {
|
for org_id in orgs {
|
||||||
let mut event = Event::new(event_type, event_date);
|
let mut event = Event::new(event_type, event_date);
|
||||||
event.user_uuid = Some(user_id.clone());
|
event.user_uuid = Some(user_id.clone());
|
||||||
event.org_uuid = Some(membership.org_uuid);
|
event.org_uuid = Some(org_id);
|
||||||
event.org_user_uuid = Some(membership.uuid);
|
|
||||||
event.act_user_uuid = Some(user_id.clone());
|
event.act_user_uuid = Some(user_id.clone());
|
||||||
event.device_type = Some(device_type);
|
event.device_type = Some(device_type);
|
||||||
event.ip_address = Some(ip.to_string());
|
event.ip_address = Some(ip.to_string());
|
||||||
|
|
|
@ -254,7 +254,7 @@ async fn leave_organization(org_id: OrganizationId, headers: Headers, mut conn:
|
||||||
}
|
}
|
||||||
|
|
||||||
log_event(
|
log_event(
|
||||||
EventType::OrganizationUserLeft as i32,
|
EventType::OrganizationUserRemoved as i32,
|
||||||
&member.uuid,
|
&member.uuid,
|
||||||
&org_id,
|
&org_id,
|
||||||
&headers.user.uuid,
|
&headers.user.uuid,
|
||||||
|
|
53
src/auth.rs
53
src/auth.rs
|
@ -620,29 +620,10 @@ pub struct OrgHeaders {
|
||||||
pub device: Device,
|
pub device: Device,
|
||||||
pub user: User,
|
pub user: User,
|
||||||
pub membership_type: MembershipType,
|
pub membership_type: MembershipType,
|
||||||
pub membership_status: MembershipStatus,
|
|
||||||
pub membership: Membership,
|
pub membership: Membership,
|
||||||
pub ip: ClientIp,
|
pub ip: ClientIp,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OrgHeaders {
|
|
||||||
fn is_member(&self) -> bool {
|
|
||||||
// NOTE: we don't care about MembershipStatus at the moment because this is only used
|
|
||||||
// where an invited, accepted or confirmed user is expected if this ever changes or
|
|
||||||
// if from_i32 is changed to return Some(Revoked) this check needs to be changed accordingly
|
|
||||||
self.membership_type >= MembershipType::User
|
|
||||||
}
|
|
||||||
fn is_confirmed_and_admin(&self) -> bool {
|
|
||||||
self.membership_status == MembershipStatus::Confirmed && self.membership_type >= MembershipType::Admin
|
|
||||||
}
|
|
||||||
fn is_confirmed_and_manager(&self) -> bool {
|
|
||||||
self.membership_status == MembershipStatus::Confirmed && self.membership_type >= MembershipType::Manager
|
|
||||||
}
|
|
||||||
fn is_confirmed_and_owner(&self) -> bool {
|
|
||||||
self.membership_status == MembershipStatus::Confirmed && self.membership_type == MembershipType::Owner
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rocket::async_trait]
|
#[rocket::async_trait]
|
||||||
impl<'r> FromRequest<'r> for OrgHeaders {
|
impl<'r> FromRequest<'r> for OrgHeaders {
|
||||||
type Error = &'static str;
|
type Error = &'static str;
|
||||||
|
@ -671,8 +652,15 @@ impl<'r> FromRequest<'r> for OrgHeaders {
|
||||||
};
|
};
|
||||||
|
|
||||||
let user = headers.user;
|
let user = headers.user;
|
||||||
let Some(membership) = Membership::find_by_user_and_org(&user.uuid, &org_id, &mut conn).await else {
|
let membership = match Membership::find_by_user_and_org(&user.uuid, &org_id, &mut conn).await {
|
||||||
err_handler!("The current user isn't member of the organization");
|
Some(member) => {
|
||||||
|
if member.status == MembershipStatus::Confirmed as i32 {
|
||||||
|
member
|
||||||
|
} else {
|
||||||
|
err_handler!("The current user isn't confirmed member of the organization")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => err_handler!("The current user isn't member of the organization"),
|
||||||
};
|
};
|
||||||
|
|
||||||
Outcome::Success(Self {
|
Outcome::Success(Self {
|
||||||
|
@ -680,22 +668,13 @@ impl<'r> FromRequest<'r> for OrgHeaders {
|
||||||
device: headers.device,
|
device: headers.device,
|
||||||
user,
|
user,
|
||||||
membership_type: {
|
membership_type: {
|
||||||
if let Some(member_type) = MembershipType::from_i32(membership.atype) {
|
if let Some(org_usr_type) = MembershipType::from_i32(membership.atype) {
|
||||||
member_type
|
org_usr_type
|
||||||
} else {
|
} else {
|
||||||
// This should only happen if the DB is corrupted
|
// This should only happen if the DB is corrupted
|
||||||
err_handler!("Unknown user type in the database")
|
err_handler!("Unknown user type in the database")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
membership_status: {
|
|
||||||
if let Some(member_status) = MembershipStatus::from_i32(membership.status) {
|
|
||||||
// NOTE: add additional check for revoked if from_i32 is ever changed
|
|
||||||
// to return Revoked status.
|
|
||||||
member_status
|
|
||||||
} else {
|
|
||||||
err_handler!("User status is either revoked or invalid.")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
membership,
|
membership,
|
||||||
ip: headers.ip,
|
ip: headers.ip,
|
||||||
})
|
})
|
||||||
|
@ -720,7 +699,7 @@ impl<'r> FromRequest<'r> for AdminHeaders {
|
||||||
|
|
||||||
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
||||||
let headers = try_outcome!(OrgHeaders::from_request(request).await);
|
let headers = try_outcome!(OrgHeaders::from_request(request).await);
|
||||||
if headers.is_confirmed_and_admin() {
|
if headers.membership_type >= MembershipType::Admin {
|
||||||
Outcome::Success(Self {
|
Outcome::Success(Self {
|
||||||
host: headers.host,
|
host: headers.host,
|
||||||
device: headers.device,
|
device: headers.device,
|
||||||
|
@ -782,7 +761,7 @@ impl<'r> FromRequest<'r> for ManagerHeaders {
|
||||||
|
|
||||||
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
||||||
let headers = try_outcome!(OrgHeaders::from_request(request).await);
|
let headers = try_outcome!(OrgHeaders::from_request(request).await);
|
||||||
if headers.is_confirmed_and_manager() {
|
if headers.membership_type >= MembershipType::Manager {
|
||||||
match get_col_id(request) {
|
match get_col_id(request) {
|
||||||
Some(col_id) => {
|
Some(col_id) => {
|
||||||
let mut conn = match DbConn::from_request(request).await {
|
let mut conn = match DbConn::from_request(request).await {
|
||||||
|
@ -837,7 +816,7 @@ impl<'r> FromRequest<'r> for ManagerHeadersLoose {
|
||||||
|
|
||||||
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
||||||
let headers = try_outcome!(OrgHeaders::from_request(request).await);
|
let headers = try_outcome!(OrgHeaders::from_request(request).await);
|
||||||
if headers.is_confirmed_and_manager() {
|
if headers.membership_type >= MembershipType::Manager {
|
||||||
Outcome::Success(Self {
|
Outcome::Success(Self {
|
||||||
host: headers.host,
|
host: headers.host,
|
||||||
device: headers.device,
|
device: headers.device,
|
||||||
|
@ -900,7 +879,7 @@ impl<'r> FromRequest<'r> for OwnerHeaders {
|
||||||
|
|
||||||
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
||||||
let headers = try_outcome!(OrgHeaders::from_request(request).await);
|
let headers = try_outcome!(OrgHeaders::from_request(request).await);
|
||||||
if headers.is_confirmed_and_owner() {
|
if headers.membership_type == MembershipType::Owner {
|
||||||
Outcome::Success(Self {
|
Outcome::Success(Self {
|
||||||
device: headers.device,
|
device: headers.device,
|
||||||
user: headers.user,
|
user: headers.user,
|
||||||
|
@ -925,7 +904,7 @@ impl<'r> FromRequest<'r> for OrgMemberHeaders {
|
||||||
|
|
||||||
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
||||||
let headers = try_outcome!(OrgHeaders::from_request(request).await);
|
let headers = try_outcome!(OrgHeaders::from_request(request).await);
|
||||||
if headers.is_member() {
|
if headers.membership_type >= MembershipType::User {
|
||||||
Outcome::Success(Self {
|
Outcome::Success(Self {
|
||||||
host: headers.host,
|
host: headers.host,
|
||||||
user: headers.user,
|
user: headers.user,
|
||||||
|
|
|
@ -150,7 +150,6 @@ impl AuthRequest {
|
||||||
auth_requests::table
|
auth_requests::table
|
||||||
.filter(auth_requests::user_uuid.eq(user_uuid))
|
.filter(auth_requests::user_uuid.eq(user_uuid))
|
||||||
.filter(auth_requests::request_device_identifier.eq(device_uuid))
|
.filter(auth_requests::request_device_identifier.eq(device_uuid))
|
||||||
.filter(auth_requests::approved.is_null())
|
|
||||||
.order_by(auth_requests::creation_date.desc())
|
.order_by(auth_requests::creation_date.desc())
|
||||||
.first::<AuthRequestDb>(conn).ok().from_db()
|
.first::<AuthRequestDb>(conn).ok().from_db()
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -142,7 +142,7 @@ impl Cipher {
|
||||||
sync_type: CipherSyncType,
|
sync_type: CipherSyncType,
|
||||||
conn: &mut DbConn,
|
conn: &mut DbConn,
|
||||||
) -> Value {
|
) -> Value {
|
||||||
use crate::util::{format_date, validate_and_format_date};
|
use crate::util::format_date;
|
||||||
|
|
||||||
let mut attachments_json: Value = Value::Null;
|
let mut attachments_json: Value = Value::Null;
|
||||||
if let Some(cipher_sync_data) = cipher_sync_data {
|
if let Some(cipher_sync_data) = cipher_sync_data {
|
||||||
|
@ -220,7 +220,7 @@ impl Cipher {
|
||||||
})
|
})
|
||||||
.map(|mut d| match d.get("lastUsedDate").and_then(|l| l.as_str()) {
|
.map(|mut d| match d.get("lastUsedDate").and_then(|l| l.as_str()) {
|
||||||
Some(l) => {
|
Some(l) => {
|
||||||
d["lastUsedDate"] = json!(validate_and_format_date(l));
|
d["lastUsedDate"] = json!(crate::util::validate_and_format_date(l));
|
||||||
d
|
d
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -261,11 +261,6 @@ impl Cipher {
|
||||||
type_data_json["uri"] = uris[0]["uri"].clone();
|
type_data_json["uri"] = uris[0]["uri"].clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if `passwordRevisionDate` is a valid date, else convert it
|
|
||||||
if let Some(pw_revision) = type_data_json["passwordRevisionDate"].as_str() {
|
|
||||||
type_data_json["passwordRevisionDate"] = json!(validate_and_format_date(pw_revision));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix secure note issues when data is invalid
|
// Fix secure note issues when data is invalid
|
||||||
|
|
|
@ -49,8 +49,6 @@ pub enum EventType {
|
||||||
UserClientExportedVault = 1007,
|
UserClientExportedVault = 1007,
|
||||||
// UserUpdatedTempPassword = 1008, // Not supported
|
// UserUpdatedTempPassword = 1008, // Not supported
|
||||||
// UserMigratedKeyToKeyConnector = 1009, // Not supported
|
// UserMigratedKeyToKeyConnector = 1009, // Not supported
|
||||||
UserRequestedDeviceApproval = 1010,
|
|
||||||
// UserTdeOffboardingPasswordSet = 1011, // Not supported
|
|
||||||
|
|
||||||
// Cipher
|
// Cipher
|
||||||
CipherCreated = 1100,
|
CipherCreated = 1100,
|
||||||
|
@ -71,7 +69,6 @@ pub enum EventType {
|
||||||
CipherSoftDeleted = 1115,
|
CipherSoftDeleted = 1115,
|
||||||
CipherRestored = 1116,
|
CipherRestored = 1116,
|
||||||
CipherClientToggledCardNumberVisible = 1117,
|
CipherClientToggledCardNumberVisible = 1117,
|
||||||
CipherClientToggledTOTPSeedVisible = 1118,
|
|
||||||
|
|
||||||
// Collection
|
// Collection
|
||||||
CollectionCreated = 1300,
|
CollectionCreated = 1300,
|
||||||
|
@ -97,10 +94,6 @@ pub enum EventType {
|
||||||
// OrganizationUserFirstSsoLogin = 1510, // Not supported
|
// OrganizationUserFirstSsoLogin = 1510, // Not supported
|
||||||
OrganizationUserRevoked = 1511,
|
OrganizationUserRevoked = 1511,
|
||||||
OrganizationUserRestored = 1512,
|
OrganizationUserRestored = 1512,
|
||||||
OrganizationUserApprovedAuthRequest = 1513,
|
|
||||||
OrganizationUserRejectedAuthRequest = 1514,
|
|
||||||
OrganizationUserDeleted = 1515,
|
|
||||||
OrganizationUserLeft = 1516,
|
|
||||||
|
|
||||||
// Organization
|
// Organization
|
||||||
OrganizationUpdated = 1600,
|
OrganizationUpdated = 1600,
|
||||||
|
@ -112,7 +105,6 @@ pub enum EventType {
|
||||||
// OrganizationEnabledKeyConnector = 1606, // Not supported
|
// OrganizationEnabledKeyConnector = 1606, // Not supported
|
||||||
// OrganizationDisabledKeyConnector = 1607, // Not supported
|
// OrganizationDisabledKeyConnector = 1607, // Not supported
|
||||||
// OrganizationSponsorshipsSynced = 1608, // Not supported
|
// OrganizationSponsorshipsSynced = 1608, // Not supported
|
||||||
// OrganizationCollectionManagementUpdated = 1609, // Not supported
|
|
||||||
|
|
||||||
// Policy
|
// Policy
|
||||||
PolicyUpdated = 1700,
|
PolicyUpdated = 1700,
|
||||||
|
@ -125,13 +117,6 @@ pub enum EventType {
|
||||||
// ProviderOrganizationAdded = 1901, // Not supported
|
// ProviderOrganizationAdded = 1901, // Not supported
|
||||||
// ProviderOrganizationRemoved = 1902, // Not supported
|
// ProviderOrganizationRemoved = 1902, // Not supported
|
||||||
// ProviderOrganizationVaultAccessed = 1903, // Not supported
|
// ProviderOrganizationVaultAccessed = 1903, // Not supported
|
||||||
|
|
||||||
// OrganizationDomainAdded = 2000, // Not supported
|
|
||||||
// OrganizationDomainRemoved = 2001, // Not supported
|
|
||||||
// OrganizationDomainVerified = 2002, // Not supported
|
|
||||||
// OrganizationDomainNotVerified = 2003, // Not supported
|
|
||||||
|
|
||||||
// SecretRetrieved = 2100, // Not supported
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Local methods
|
/// Local methods
|
||||||
|
|
|
@ -55,7 +55,6 @@ db_object! {
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/bitwarden/server/blob/b86a04cef9f1e1b82cf18e49fc94e017c641130c/src/Core/Enums/OrganizationUserStatusType.cs
|
// https://github.com/bitwarden/server/blob/b86a04cef9f1e1b82cf18e49fc94e017c641130c/src/Core/Enums/OrganizationUserStatusType.cs
|
||||||
#[derive(PartialEq)]
|
|
||||||
pub enum MembershipStatus {
|
pub enum MembershipStatus {
|
||||||
Revoked = -1,
|
Revoked = -1,
|
||||||
Invited = 0,
|
Invited = 0,
|
||||||
|
@ -63,19 +62,6 @@ pub enum MembershipStatus {
|
||||||
Confirmed = 2,
|
Confirmed = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MembershipStatus {
|
|
||||||
pub fn from_i32(status: i32) -> Option<Self> {
|
|
||||||
match status {
|
|
||||||
0 => Some(Self::Invited),
|
|
||||||
1 => Some(Self::Accepted),
|
|
||||||
2 => Some(Self::Confirmed),
|
|
||||||
// NOTE: we don't care about revoked members where this is used
|
|
||||||
// if this ever changes also adapt the OrgHeaders check.
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, num_derive::FromPrimitive)]
|
#[derive(Copy, Clone, PartialEq, Eq, num_derive::FromPrimitive)]
|
||||||
pub enum MembershipType {
|
pub enum MembershipType {
|
||||||
Owner = 0,
|
Owner = 0,
|
||||||
|
|
|
@ -55,10 +55,7 @@ impl Fairing for AppHeaders {
|
||||||
res.set_raw_header("Referrer-Policy", "same-origin");
|
res.set_raw_header("Referrer-Policy", "same-origin");
|
||||||
res.set_raw_header("X-Content-Type-Options", "nosniff");
|
res.set_raw_header("X-Content-Type-Options", "nosniff");
|
||||||
res.set_raw_header("X-Robots-Tag", "noindex, nofollow");
|
res.set_raw_header("X-Robots-Tag", "noindex, nofollow");
|
||||||
|
res.set_raw_header("Cross-Origin-Resource-Policy", "same-origin");
|
||||||
if !res.headers().get_one("Content-Type").is_some_and(|v| v.starts_with("image/")) {
|
|
||||||
res.set_raw_header("Cross-Origin-Resource-Policy", "same-origin");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Obsolete in modern browsers, unsafe (XS-Leak), and largely replaced by CSP
|
// Obsolete in modern browsers, unsafe (XS-Leak), and largely replaced by CSP
|
||||||
res.set_raw_header("X-XSS-Protection", "0");
|
res.set_raw_header("X-XSS-Protection", "0");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue