mirror of
https://github.com/minio/minio.git
synced 2024-12-24 22:25:54 -05:00
reject object names with '\' on windows (#16856)
This commit is contained in:
parent
6c11dbffd5
commit
b3c54ec81e
@ -22,6 +22,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -32,6 +33,9 @@ import (
|
|||||||
|
|
||||||
// Wrapper for calling NewMultipartUpload tests for both Erasure multiple disks and single node setup.
|
// Wrapper for calling NewMultipartUpload tests for both Erasure multiple disks and single node setup.
|
||||||
func TestObjectNewMultipartUpload(t *testing.T) {
|
func TestObjectNewMultipartUpload(t *testing.T) {
|
||||||
|
if runtime.GOOS == globalWindowsOSName {
|
||||||
|
t.Skip()
|
||||||
|
}
|
||||||
ExecObjectLayerTest(t, testObjectNewMultipartUpload)
|
ExecObjectLayerTest(t, testObjectNewMultipartUpload)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,9 +114,18 @@ func testObjectAbortMultipartUpload(obj ObjectLayer, instanceType string, t Test
|
|||||||
{"--", object, uploadID, BucketNotFound{}},
|
{"--", object, uploadID, BucketNotFound{}},
|
||||||
{"foo", object, uploadID, BucketNotFound{}},
|
{"foo", object, uploadID, BucketNotFound{}},
|
||||||
{bucket, object, "foo-foo", InvalidUploadID{}},
|
{bucket, object, "foo-foo", InvalidUploadID{}},
|
||||||
{bucket, "\\", uploadID, InvalidUploadID{}},
|
|
||||||
{bucket, object, uploadID, nil},
|
{bucket, object, uploadID, nil},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if runtime.GOOS != globalWindowsOSName {
|
||||||
|
abortTestCases = append(abortTestCases, struct {
|
||||||
|
bucketName string
|
||||||
|
objName string
|
||||||
|
uploadID string
|
||||||
|
expectedErrType error
|
||||||
|
}{bucket, "\\", uploadID, InvalidUploadID{}})
|
||||||
|
}
|
||||||
|
|
||||||
// Iterating over creatPartCases to generate multipart chunks.
|
// Iterating over creatPartCases to generate multipart chunks.
|
||||||
for i, testCase := range abortTestCases {
|
for i, testCase := range abortTestCases {
|
||||||
err = obj.AbortMultipartUpload(context.Background(), testCase.bucketName, testCase.objName, testCase.uploadID, opts)
|
err = obj.AbortMultipartUpload(context.Background(), testCase.bucketName, testCase.objName, testCase.uploadID, opts)
|
||||||
|
@ -174,10 +174,7 @@ func IsValidObjectPrefix(object string) bool {
|
|||||||
// work with file systems, we will reject here
|
// work with file systems, we will reject here
|
||||||
// to return object name invalid rather than
|
// to return object name invalid rather than
|
||||||
// a cryptic error from the file system.
|
// a cryptic error from the file system.
|
||||||
if strings.ContainsRune(object, 0) {
|
return !strings.ContainsRune(object, 0)
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkObjectNameForLengthAndSlash -check for the validity of object name length and prefis as slash
|
// checkObjectNameForLengthAndSlash -check for the validity of object name length and prefis as slash
|
||||||
@ -199,7 +196,7 @@ func checkObjectNameForLengthAndSlash(bucket, object string) error {
|
|||||||
if runtime.GOOS == globalWindowsOSName {
|
if runtime.GOOS == globalWindowsOSName {
|
||||||
// Explicitly disallowed characters on windows.
|
// Explicitly disallowed characters on windows.
|
||||||
// Avoids most problematic names.
|
// Avoids most problematic names.
|
||||||
if strings.ContainsAny(object, `:*?"|<>`) {
|
if strings.ContainsAny(object, `\:*?"|<>`) {
|
||||||
return ObjectNameInvalid{
|
return ObjectNameInvalid{
|
||||||
Bucket: bucket,
|
Bucket: bucket,
|
||||||
Object: object,
|
Object: object,
|
||||||
|
@ -19,19 +19,75 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/klauspost/compress/s2"
|
"github.com/klauspost/compress/s2"
|
||||||
|
"github.com/minio/minio/internal/auth"
|
||||||
"github.com/minio/minio/internal/config/compress"
|
"github.com/minio/minio/internal/config/compress"
|
||||||
"github.com/minio/minio/internal/crypto"
|
"github.com/minio/minio/internal/crypto"
|
||||||
"github.com/minio/pkg/trie"
|
"github.com/minio/pkg/trie"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Wrapper
|
||||||
|
func TestPathTraversalExploit(t *testing.T) {
|
||||||
|
if runtime.GOOS != globalWindowsOSName {
|
||||||
|
t.Skip()
|
||||||
|
}
|
||||||
|
defer DetectTestLeak(t)()
|
||||||
|
ExecExtendedObjectLayerAPITest(t, testPathTraversalExploit, []string{"PutObject"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// testPathTraversal exploit test, exploits path traversal on windows
|
||||||
|
// with following object names "\\../.minio.sys/config/iam/${username}/identity.json"
|
||||||
|
// #16852
|
||||||
|
func testPathTraversalExploit(obj ObjectLayer, instanceType, bucketName string, apiRouter http.Handler,
|
||||||
|
credentials auth.Credentials, t *testing.T,
|
||||||
|
) {
|
||||||
|
if err := newTestConfig(globalMinioDefaultRegion, obj); err != nil {
|
||||||
|
t.Fatalf("Initializing config.json failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
objectName := `\../.minio.sys/config/hello.txt`
|
||||||
|
|
||||||
|
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
// construct HTTP request for Get Object end point.
|
||||||
|
req, err := newTestSignedRequestV4(http.MethodPut, getPutObjectURL("", bucketName, objectName),
|
||||||
|
int64(5), bytes.NewReader([]byte("hello")), credentials.AccessKey, credentials.SecretKey, map[string]string{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create HTTP request for Put Object: <ERROR> %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic ofthe handler.
|
||||||
|
// Call the ServeHTTP to execute the handler.
|
||||||
|
apiRouter.ServeHTTP(rec, req)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(GlobalContext)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// Now check if we actually wrote to backend (regardless of the response
|
||||||
|
// returned by the server).
|
||||||
|
z := obj.(*erasureServerPools)
|
||||||
|
xl := z.serverPools[0].sets[0]
|
||||||
|
erasureDisks := xl.getDisks()
|
||||||
|
parts, errs := readAllFileInfo(ctx, erasureDisks, bucketName, objectName, "", false)
|
||||||
|
for i := range parts {
|
||||||
|
if errs[i] == nil {
|
||||||
|
if parts[i].Name == objectName {
|
||||||
|
t.Errorf("path traversal allowed to allow writing to minioMetaBucket: %s", instanceType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Tests validate bucket name.
|
// Tests validate bucket name.
|
||||||
func TestIsValidBucketName(t *testing.T) {
|
func TestIsValidBucketName(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
|
Loading…
Reference in New Issue
Block a user