diff --git a/weed/s3api/s3api_tables.go b/weed/s3api/s3api_tables.go index 2b5d352b9..75082cf7c 100644 --- a/weed/s3api/s3api_tables.go +++ b/weed/s3api/s3api_tables.go @@ -226,17 +226,16 @@ func parseOptionalIntParam(r *http.Request, name string) (int, error) { return parsed, nil } -func parseOptionalNamespace(r *http.Request, name string) []string { +func parseOptionalNamespace(r *http.Request, name string) ([]string, error) { value := r.URL.Query().Get(name) if value == "" { - return nil + return nil, nil } parts, err := s3tables.ParseNamespace(value) if err != nil { - glog.V(1).Infof("invalid namespace value for %s: %q: %v", name, value, err) - return nil + return nil, fmt.Errorf("invalid %s: %w", name, err) } - return parts + return parts, nil } func parseRequiredNamespacePathParam(r *http.Request, name string) ([]string, error) { @@ -412,13 +411,17 @@ func buildListTablesRequest(r *http.Request) (interface{}, error) { if err != nil { return nil, err } + namespace, err := parseOptionalNamespace(r, "namespace") + if err != nil { + return nil, err + } maxTables, err := parseOptionalIntParam(r, "maxTables") if err != nil { return nil, err } return &s3tables.ListTablesRequest{ TableBucketARN: tableBucketARN, - Namespace: parseOptionalNamespace(r, "namespace"), + Namespace: namespace, Prefix: r.URL.Query().Get("prefix"), ContinuationToken: r.URL.Query().Get("continuationToken"), MaxTables: maxTables, @@ -433,7 +436,11 @@ func buildGetTableRequest(r *http.Request) (interface{}, error) { } if tableARN == "" { req.TableBucketARN = query.Get("tableBucketARN") - req.Namespace = parseOptionalNamespace(r, "namespace") + namespace, err := parseOptionalNamespace(r, "namespace") + if err != nil { + return nil, err + } + req.Namespace = namespace req.Name = query.Get("name") if req.TableBucketARN == "" || len(req.Namespace) == 0 || req.Name == "" { return nil, fmt.Errorf("either tableArn or (tableBucketARN, namespace, name) must be provided") diff --git a/weed/s3api/s3api_tables_rest_validation_test.go b/weed/s3api/s3api_tables_rest_validation_test.go new file mode 100644 index 000000000..3445e42df --- /dev/null +++ b/weed/s3api/s3api_tables_rest_validation_test.go @@ -0,0 +1,65 @@ +package s3api + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "net/url" + "strings" + "testing" + + "github.com/gorilla/mux" + + "github.com/seaweedfs/seaweedfs/weed/s3api/s3tables" +) + +const testTableBucketARN = "arn:aws:s3tables:us-east-1:123456789012:bucket/test-bucket" + +func TestBuildListTablesRequestRejectsInvalidNamespaceQuery(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "/tables?namespace=InvalidNamespace", nil) + req = mux.SetURLVars(req, map[string]string{"tableBucketARN": testTableBucketARN}) + + _, err := buildListTablesRequest(req) + if err == nil { + t.Fatalf("expected invalid namespace query to return an error") + } + if !strings.Contains(err.Error(), "invalid namespace") { + t.Fatalf("expected invalid namespace error, got %q", err.Error()) + } +} + +func TestBuildGetTableRequestRejectsInvalidNamespaceQuery(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "/get-table?tableBucketARN="+url.QueryEscape(testTableBucketARN)+"&namespace=InvalidNamespace&name=table1", nil) + + _, err := buildGetTableRequest(req) + if err == nil { + t.Fatalf("expected invalid namespace query to return an error") + } + if !strings.Contains(err.Error(), "invalid namespace") { + t.Fatalf("expected invalid namespace error, got %q", err.Error()) + } +} + +func TestHandleRestOperationReturnsBadRequestForInvalidNamespaceQuery(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "/tables?namespace=InvalidNamespace", nil) + req = mux.SetURLVars(req, map[string]string{"tableBucketARN": testTableBucketARN}) + rr := httptest.NewRecorder() + + st := &S3TablesApiServer{} + st.handleRestOperation("ListTables", buildListTablesRequest).ServeHTTP(rr, req) + + if rr.Code != http.StatusBadRequest { + t.Fatalf("expected status %d, got %d", http.StatusBadRequest, rr.Code) + } + + var body map[string]string + if err := json.Unmarshal(rr.Body.Bytes(), &body); err != nil { + t.Fatalf("failed to decode error response: %v", err) + } + if got, want := body["__type"], s3tables.ErrCodeInvalidRequest; got != want { + t.Fatalf("expected __type=%q, got %q", want, got) + } + if !strings.Contains(body["message"], "invalid namespace") { + t.Fatalf("expected invalid namespace error message, got %q", body["message"]) + } +}