mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-27 07:27:37 +00:00
Merge commit from fork
docs/testing/security-regressions.md requires every fixed advisory to map to a named, greppable regression test. Rename the tests added with the fixes to carry their advisory id so `rg -i ghsa` finds them, and add the four rows to the advisory -> test map. Adds the FTPS MKD regression test that was missing. It primes the dummy backend with a successful create_bucket, so the assertion distinguishes "denied at the authorization boundary" from "backend refused" — without the queued success an unconfigured create_bucket fails on its own and the test would pass even with the authorization check removed. Verified it fails when the check is reverted. DummyBackend gains a Debug impl (FtpsDriver's trait bounds require it) and a queue_create_bucket_ok helper. Records in the CI-execution map why ghsa_g3vq_* runs in the default pass despite sitting behind the ftps feature: the rustfs crate defaults to ["ftps", "webdav"] and cargo unifies features across the workspace build, so the test executes there even though `cargo test -p rustfs-protocols` alone would skip it.
This commit is contained in:
@@ -217,6 +217,15 @@ pub struct DummyBackend {
|
||||
inner: Mutex<Inner>,
|
||||
}
|
||||
|
||||
// Drivers whose trait bounds require `Debug` (for example FtpsDriver) cannot be
|
||||
// instantiated with this double otherwise. The queues themselves are not worth
|
||||
// rendering, and locking to print them would risk deadlocking a failing test.
|
||||
impl std::fmt::Debug for DummyBackend {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("DummyBackend").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DummyBackend {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
@@ -263,6 +272,18 @@ impl DummyBackend {
|
||||
.push_back(Ok(PutObjectOutput::default()));
|
||||
}
|
||||
|
||||
/// Queue a successful create_bucket. Authorization tests use this to tell
|
||||
/// "denied before the backend" apart from "backend refused": an unqueued
|
||||
/// create_bucket returns `Unconfigured`, so only a queued success proves the
|
||||
/// driver reached the backend.
|
||||
pub fn queue_create_bucket_ok(&self) {
|
||||
self.inner
|
||||
.lock()
|
||||
.expect("lock")
|
||||
.create_bucket
|
||||
.push_back(Ok(CreateBucketOutput::default()));
|
||||
}
|
||||
|
||||
/// Queue a put_object error. Used by the commit_write retry tests
|
||||
/// to script SlowDown / AccessDenied sequences against the
|
||||
/// rustfs_utils::retry::is_s3code_in_message_retryable predicate.
|
||||
|
||||
@@ -898,6 +898,37 @@ mod tests {
|
||||
use super::parse_s3_path;
|
||||
use rustfs_utils::path;
|
||||
|
||||
/// GHSA-g3vq-vv42-f647: MKD creates a bucket, so it must clear the
|
||||
/// `s3:CreateBucket` authorization boundary before touching the backend.
|
||||
/// The queued success is what makes this a real guard — an unqueued
|
||||
/// create_bucket would fail on its own and the test would pass even if the
|
||||
/// authorization check were removed again.
|
||||
#[tokio::test]
|
||||
async fn ghsa_g3vq_mkd_denied_before_reaching_backend() {
|
||||
use super::FtpsDriver;
|
||||
use crate::common::dummy_storage::DummyBackend;
|
||||
use crate::common::gateway::with_test_auth_override;
|
||||
use crate::common::session::{Protocol, test_session};
|
||||
use unftp_core::storage::StorageBackend as _;
|
||||
|
||||
let backend = DummyBackend::new();
|
||||
backend.queue_create_bucket_ok();
|
||||
|
||||
let driver = FtpsDriver::new(backend);
|
||||
let user = super::super::server::FtpsUser {
|
||||
username: "denied-user".to_string(),
|
||||
name: None,
|
||||
session_context: test_session(Protocol::Ftps),
|
||||
};
|
||||
|
||||
let result = with_test_auth_override(|_, _, _| false, driver.mkd(&user, "/denied-bucket")).await;
|
||||
|
||||
assert!(
|
||||
result.is_err(),
|
||||
"MKD must fail closed when authorization denies s3:CreateBucket, even though the backend was primed to succeed"
|
||||
);
|
||||
}
|
||||
|
||||
proptest::proptest! {
|
||||
#[test]
|
||||
fn parse_s3_path_never_leaks_control_bytes_or_traversal_in_ok_output(
|
||||
|
||||
Reference in New Issue
Block a user