mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-08-16 12:16:36 +00:00
* sftp: match permission paths on path-component boundaries Permission checks compared paths with raw string prefixes, so a permission entry for /tenants/alice also matched sibling paths such as /tenants/alice-archive, letting a scoped user read and overwrite another tenant's files. The home directory containment check had the same flaw. Route both through pathWithin, which cleans the paths and requires exact equality or a separator-delimited descendant. * sftp: clean permission path into a local when ranking matches
73 lines
2.2 KiB
Go
73 lines
2.2 KiB
Go
package sftp
|
|
|
|
import (
|
|
"io"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// TestScopedUserCannotAccessSiblingPrefix verifies that a permission entry
|
|
// for /tenants/alice does not leak onto sibling paths such as
|
|
// /tenants/alice-archive that merely share the string prefix.
|
|
func TestScopedUserCannotAccessSiblingPrefix(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping integration test in short mode")
|
|
}
|
|
|
|
config := DefaultTestConfig()
|
|
config.EnableDebug = testing.Verbose()
|
|
|
|
fw := NewSftpTestFramework(t, config)
|
|
require.NoError(t, fw.Setup(config))
|
|
defer fw.Cleanup()
|
|
|
|
// admin seeds both tenants
|
|
admin, adminConn, err := fw.ConnectSFTP("admin", "adminpassword")
|
|
require.NoError(t, err)
|
|
defer adminConn.Close()
|
|
defer admin.Close()
|
|
|
|
require.NoError(t, admin.MkdirAll("/tenants/alice"))
|
|
require.NoError(t, admin.MkdirAll("/tenants/alice-archive"))
|
|
secret, err := admin.Create("/tenants/alice-archive/secret.txt")
|
|
require.NoError(t, err)
|
|
_, err = secret.Write([]byte("SIBLING-SECRET"))
|
|
require.NoError(t, err)
|
|
require.NoError(t, secret.Close())
|
|
|
|
scoped, scopedConn, err := fw.ConnectSFTP("scoped", "scopedpassword")
|
|
require.NoError(t, err)
|
|
defer scopedConn.Close()
|
|
defer scoped.Close()
|
|
|
|
// positive control: the scoped user can write within its own prefix
|
|
own, err := scoped.Create("/tenants/alice/own.txt")
|
|
require.NoError(t, err)
|
|
_, err = own.Write([]byte("ok"))
|
|
require.NoError(t, err)
|
|
require.NoError(t, own.Close())
|
|
|
|
// reading the sibling tenant's file must fail
|
|
if f, err := scoped.Open("/tenants/alice-archive/secret.txt"); err == nil {
|
|
data, _ := io.ReadAll(f)
|
|
f.Close()
|
|
t.Fatalf("ACL for /tenants/alice read sibling /tenants/alice-archive: %q", data)
|
|
}
|
|
|
|
// overwriting the sibling tenant's file must fail
|
|
if f, err := scoped.Create("/tenants/alice-archive/secret.txt"); err == nil {
|
|
f.Write([]byte("OVERWRITTEN-BY-SCOPED-USER"))
|
|
f.Close()
|
|
t.Fatal("ACL for /tenants/alice overwrote sibling /tenants/alice-archive")
|
|
}
|
|
|
|
// the sibling file is intact
|
|
f, err := admin.Open("/tenants/alice-archive/secret.txt")
|
|
require.NoError(t, err)
|
|
data, err := io.ReadAll(f)
|
|
require.NoError(t, err)
|
|
require.NoError(t, f.Close())
|
|
require.Equal(t, "SIBLING-SECRET", string(data))
|
|
}
|