From 8361476d04fac1349c7603ba3d8c390ab2d91ed4 Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Thu, 30 Jul 2026 21:43:44 +0200 Subject: [PATCH] fix(rest): validate namespace identifier components Signed-off-by: Minh Vu --- catalog/catalog.go | 13 +++++++++++++ catalog/identifier_test.go | 18 ++++++++++++++++++ catalog/rest/rest.go | 6 +----- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/catalog/catalog.go b/catalog/catalog.go index ccc0e535f..aa49739c5 100644 --- a/catalog/catalog.go +++ b/catalog/catalog.go @@ -255,6 +255,10 @@ func validateIdentifier(ident table.Identifier, notFoundErr error) error { notFoundErr, strings.Join(ident, ".")) } + return validateIdentifierComponents(ident, notFoundErr) +} + +func validateIdentifierComponents(ident table.Identifier, notFoundErr error) error { for _, part := range ident { if part == "" || part == "." || part == ".." || strings.Contains(part, "/") { return fmt.Errorf("%w: invalid identifier component %q in %v", @@ -272,6 +276,15 @@ func validateIdentifier(ident table.Identifier, notFoundErr error) error { return nil } +// ValidateNamespaceIdentifier checks that an identifier contains at least one valid namespace level. +func ValidateNamespaceIdentifier(ident table.Identifier) error { + if len(ident) < 1 { + return fmt.Errorf("%w: empty namespace identifier", ErrNoSuchNamespace) + } + + return validateIdentifierComponents(ident, ErrNoSuchNamespace) +} + // ValidateTableIdentifier checks that an identifier contains at least one valid namespace level and a table name. func ValidateTableIdentifier(ident table.Identifier) error { return validateIdentifier(ident, ErrNoSuchTable) diff --git a/catalog/identifier_test.go b/catalog/identifier_test.go index f6a5afa8f..5ea5aebf1 100644 --- a/catalog/identifier_test.go +++ b/catalog/identifier_test.go @@ -45,6 +45,24 @@ func TestValidateTableIdentifier(t *testing.T) { } } +func TestValidateNamespaceIdentifier(t *testing.T) { + require.NoError(t, catalog.ValidateNamespaceIdentifier(table.Identifier{"namespace"})) + require.NoError(t, catalog.ValidateNamespaceIdentifier(table.Identifier{"parent", "namespace"})) + + for _, ident := range []table.Identifier{ + nil, + {}, + {""}, + {"."}, + {".."}, + {"namespace/child"}, + {"namespace\nchild"}, + {"parent", ""}, + } { + require.ErrorIs(t, catalog.ValidateNamespaceIdentifier(ident), catalog.ErrNoSuchNamespace) + } +} + func TestNamespaceFromEmptyIdentifier(t *testing.T) { require.Nil(t, catalog.NamespaceFromIdent(nil)) require.Nil(t, catalog.NamespaceFromIdent(table.Identifier{})) diff --git a/catalog/rest/rest.go b/catalog/rest/rest.go index ebda90efd..c0d555c34 100644 --- a/catalog/rest/rest.go +++ b/catalog/rest/rest.go @@ -1075,11 +1075,7 @@ func (r *Catalog) Close() error { return r.reporter.Close() } var _ catalog.Closer = (*Catalog)(nil) func checkValidNamespace(ident table.Identifier) error { - if len(ident) < 1 { - return fmt.Errorf("%w: empty namespace identifier", catalog.ErrNoSuchNamespace) - } - - return nil + return catalog.ValidateNamespaceIdentifier(ident) } func (r *Catalog) tableFromResponse(_ context.Context, identifier []string, metadata table.Metadata, loc string, config iceberg.Properties, credsVended bool) (*table.Table, error) {