fix(security): add IAM authorization to FTP read/metadata/cwd handlers (GHSA-3g29-xff2-92vp) (#3347)

fix(security): add IAM authorization to FTP read/metadata/cwd handlers

The FTP frontend's get() (RETR), metadata() (SIZE/MDTM), and cwd()
(CWD) handlers dispatched directly to the storage backend without
calling authorize_operation(). This allowed any authenticated FTP user,
including those with explicit Deny policies, to read arbitrary objects
and probe bucket existence regardless of IAM policy.

Add authorize_operation() calls matching the pattern used by the
write-path handlers (put, del, list, rmd) and the WebDAV driver:
- get(): S3Action::GetObject
- metadata() for files: S3Action::HeadObject
- metadata() for directories: S3Action::HeadBucket
- cwd(): S3Action::HeadBucket

Fixes: GHSA-3g29-xff2-92vp

Co-authored-by: houseme <housemecn@gmail.com>
This commit is contained in:
安正超
2026-06-11 13:48:53 +08:00
committed by GitHub
parent d4ee44f49f
commit d60deba9b6
2 changed files with 22 additions and 4 deletions
+20
View File
@@ -233,6 +233,11 @@ where
.map_err(|e| Error::new(ErrorKind::PermanentFileNotAvailable, format!("{}: {}", "Invalid path", e)))?;
if let Some(key) = key {
// Authorize HeadObject
authorize_operation(session_context, &S3Action::HeadObject, &bucket, Some(&key))
.await
.map_err(|_| Error::new(ErrorKind::PermanentFileNotAvailable, "Access denied"))?;
match self
.storage
.head_object(
@@ -264,6 +269,11 @@ where
}
} else {
// Directory metadata - use HeadBucket
// Authorize HeadBucket
authorize_operation(session_context, &S3Action::HeadBucket, &bucket, None)
.await
.map_err(|_| Error::new(ErrorKind::PermanentFileNotAvailable, "Access denied"))?;
let bucket_clone = bucket.clone();
match self
.storage
@@ -434,6 +444,11 @@ where
let key = key.ok_or_else(|| Error::new(ErrorKind::PermanentFileNotAvailable, "Cannot get directory"))?;
// Authorize GetObject
authorize_operation(session_context, &S3Action::GetObject, &bucket, Some(&key))
.await
.map_err(|_| Error::new(ErrorKind::PermanentFileNotAvailable, "Access denied"))?;
match self
.storage
.get_object(
@@ -668,6 +683,11 @@ where
.parse_s3_path(&path_str)
.map_err(|e| Error::new(ErrorKind::PermanentFileNotAvailable, format!("{}: {}", "Invalid path", e)))?;
// Authorize HeadBucket (CWD probes bucket existence)
authorize_operation(session_context, &S3Action::HeadBucket, &bucket, None)
.await
.map_err(|_| Error::new(ErrorKind::PermanentFileNotAvailable, "Access denied"))?;
// Check if bucket exists
match self
.storage