laskoviymishka commented on code in PR #1228:
URL: https://github.com/apache/iceberg-go/pull/1228#discussion_r3448961945


##########
catalog/hadoop/hadoop_test.go:
##########
@@ -999,9 +1024,121 @@ func (s *HadoopCatalogTestSuite) 
TestCreateTableShortIdentifier() {
 
        _, err := s.cat.CreateTable(ctx, []string{"tbl"}, s.testSchema())
        s.Require().Error(err)
+       s.ErrorIs(err, catalog.ErrNoSuchTable)
        s.Contains(err.Error(), "at least a namespace and table name")
 }
 
+func (s *HadoopCatalogTestSuite) tablePathOperations(ctx context.Context) 
[]struct {
+       name string
+       run  func(table.Identifier) error
+} {
+       return []struct {
+               name string
+               run  func(table.Identifier) error
+       }{
+               {
+                       name: "CreateTable",
+                       run: func(ident table.Identifier) error {
+                               _, err := s.cat.CreateTable(ctx, ident, 
s.testSchema())
+
+                               return err
+                       },
+               },
+               {
+                       name: "LoadTable",
+                       run: func(ident table.Identifier) error {
+                               _, err := s.cat.LoadTable(ctx, ident)
+
+                               return err
+                       },
+               },
+               {
+                       name: "CheckTableExists",
+                       run: func(ident table.Identifier) error {
+                               _, err := s.cat.CheckTableExists(ctx, ident)
+
+                               return err
+                       },
+               },
+               {
+                       name: "CommitTable",
+                       run: func(ident table.Identifier) error {
+                               _, _, err := s.cat.CommitTable(ctx, ident, nil, 
nil)
+
+                               return err
+                       },
+               },
+               {
+                       name: "DropTable",
+                       run: func(ident table.Identifier) error {
+                               return s.cat.DropTable(ctx, ident)
+                       },
+               },
+               {
+                       name: "PurgeTable",
+                       run: func(ident table.Identifier) error {
+                               return s.cat.PurgeTable(ctx, ident)
+                       },
+               },
+       }
+}
+
+func (s *HadoopCatalogTestSuite) TestTableOperationsRejectInvalidIdentifiers() 
{
+       ctx := context.Background()
+       tests := []struct {
+               name  string
+               ident table.Identifier
+       }{
+               {"empty namespace component", table.Identifier{"", "tbl"}},
+               {"empty table name", table.Identifier{"ns", ""}},
+               {"dot namespace component", table.Identifier{".", "tbl"}},
+               {"dot table name", table.Identifier{"ns", "."}},
+               {"dot dot namespace component", table.Identifier{"..", "tbl"}},
+               {"dot dot table name", table.Identifier{"ns", ".."}},
+               {"slash namespace component", table.Identifier{"a/b", "tbl"}},
+               {"slash table name", table.Identifier{"ns", "a/b"}},
+               {"backslash namespace component", table.Identifier{`a\b`, 
"tbl"}},
+               {"backslash table name", table.Identifier{"ns", `a\b`}},
+       }
+
+       for _, tt := range tests {
+               s.Run(tt.name, func() {
+                       for _, op := range s.tablePathOperations(ctx) {
+                               s.Run(op.name, func() {
+                                       err := op.run(tt.ident)
+                                       s.Require().Error(err)
+                                       s.ErrorIs(err, catalog.ErrNoSuchTable)
+                               })
+                       }
+               })

Review Comment:
   This test doesn't actually exercise the warehouse-boundary check it's named 
for.
   
   The `".."` component is rejected by `validateTableIdentifier` before 
`validateWarehousePath` is ever reached — if you deleted 
`validateWarehousePath` entirely, every assertion here still passes. There's 
currently no input that reaches the warehouse check having passed identifier 
validation, which is itself the evidence that the check is redundant (see the 
note on `validateWarehousePath`).
   
   If `validateWarehousePath` survives as a real symlink guard, I'd add a case 
that actually escapes through a symlink. If it doesn't, I'd rename this to 
reflect that it's documenting identifier-validation behavior. While we're here, 
the inner loop doesn't wrap `op.run` in `s.Run(op.name, ...)` like 
`TestTableOperationsRejectInvalidIdentifiers` does, so a failure won't name 
which op broke.



##########
catalog/hadoop/hadoop.go:
##########
@@ -397,16 +443,16 @@ func (c *Catalog) LoadTable(ctx context.Context, ident 
table.Identifier) (*table
 }
 
 func (c *Catalog) CheckTableExists(_ context.Context, ident table.Identifier) 
(bool, error) {
-       if len(ident) < 2 {
-               return false, nil
+       if err := c.validateTablePath(ident); err != nil {
+               return false, err

Review Comment:
   This flips the contract for invalid identifiers from `(false, nil)` to 
`(false, err)`.
   
   `(false, nil)` is the canonical "definitively does not exist" answer, and 
the REST catalog in this repo deliberately returns `false, nil` on 
`ErrNoSuchTable` (the `CheckTableExists` path in rest.go). Making Hadoop return 
an error for a trivially-invalid input means callers guarding `if err != nil` 
now hit the error branch on what used to be a benign "no". I'd keep 
`CheckTableExists` returning `false, nil` for identifier-validation failures — 
a pure existence check shouldn't error on garbage input — and leave the 
erroring behavior to the write/load ops.



##########
catalog/hadoop/hadoop.go:
##########
@@ -164,6 +176,40 @@ func (c *Catalog) tableToPath(ident table.Identifier) 
string {
        return filepath.Join(append([]string{c.warehouse}, ident...)...)
 }
 
+func (c *Catalog) validateWarehousePath(path string, errType error, objectType 
string) error {
+       absPath, err := filepath.Abs(path)

Review Comment:
   I don't think this guard buys what the PR description says it does.
   
   `filepath.Abs` and `filepath.Rel` are both purely lexical — neither 
dereferences symlinks. By the time we reach here, `validateTableIdentifier` has 
already rejected every component that could escape (`.`, `..`, `/`, `\`), so 
for any input that gets this far the relative path is guaranteed to stay under 
the warehouse. The one case it can't catch — a symlink inside the warehouse 
pointing out — is exactly the case lexical resolution misses.
   
   So as written it's effectively dead code with a security rationale, which is 
the risky kind: someone later drops the component checks assuming the path 
guard covers traversal. I'd either make it real with `filepath.EvalSymlinks` 
(falling back to the lexical checks when the target doesn't exist yet, since 
the table dir won't exist on create), or drop it and add a comment that symlink 
escapes are out of scope. wdyt?



##########
catalog/hadoop/hadoop.go:
##########
@@ -164,6 +176,40 @@ func (c *Catalog) tableToPath(ident table.Identifier) 
string {
        return filepath.Join(append([]string{c.warehouse}, ident...)...)
 }
 
+func (c *Catalog) validateWarehousePath(path string, errType error, objectType 
string) error {
+       absPath, err := filepath.Abs(path)
+       if err != nil {
+               return fmt.Errorf("%w: failed to resolve %s path: %v", errType, 
objectType, err)
+       }

Review Comment:
   `errType` is wrapped with `%w` but the underlying OS error is dropped with 
`%v` here and on the `filepath.Rel` branch below — so `errors.Is(err, 
fs.ErrPermission)` and friends are always false. Go 1.20+ allows multiple `%w` 
in one `Errorf`, so I'd switch both to `%w`. This is also the classic errorlint 
pattern that `.golangci.yml` flags, so CI will fail on it as-is.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to