jerryshao commented on code in PR #13721:
URL: https://github.com/apache/gravitino/pull/13721#discussion_r4245334783


##########
docs/open-api/filesets.yaml:
##########
@@ -550,6 +564,19 @@ components:
       }
 
   responses:
+    InvalidSubPathErrorResponse:
+      description: >-
+        The sub path does not satisfy the fileset path requirements.
+        For file listing, this response also applies when the sub path does 
not exist.

Review Comment:
   [Nit] This listing-only sentence also shows up on the location endpoint.
   
   `InvalidSubPathErrorResponse` is referenced by both operations 
(filesets.yaml:195-196 for `/location` and :250-251 for `/files`), and the spec 
is OpenAPI 3.0.3 (docs/open-api/openapi.yaml:19), where a `description` written 
next to a `$ref` is ignored. So a reader of `GET .../location` is told "For 
file listing, this response also applies when the sub path does not exist", 
which does not apply to that endpoint - `getFileLocation` never touches a 
filesystem and so never reports a missing path.
   
   Suggest either keeping the shared component generic and stating the 
existence condition in the `files` endpoint's own `sub_path` description 
(filesets.yaml:235-240, already being reworded in this PR), or defining a 
second response component for the listing endpoint.
   
   Verified by: read both `$ref` sites and the component in this checkout, and 
confirmed the spec version in docs/open-api/openapi.yaml; `yaml.safe_load` on 
filesets.yaml parses and the component resolves.



##########
catalogs/catalog-fileset/src/main/java/org/apache/gravitino/catalog/fileset/FilesetCatalogOperations.java:
##########
@@ -1796,11 +1796,16 @@ private String failureCategory(Throwable throwable) {
 
   private String getFileLocation(Fileset fileset, String subPath, String 
locationName) {
     Preconditions.checkArgument(subPath != null, "subPath must not be null");
-    String processedSubPath;
-    if (!subPath.trim().isEmpty() && !subPath.trim().startsWith(SLASH)) {
-      processedSubPath = SLASH + subPath.trim();
-    } else {
-      processedSubPath = subPath.trim();
+    String processedSubPath = subPath.trim();
+    // Validate before constructing a Hadoop Path, which normalizes away 
parent directory segments.
+    Preconditions.checkArgument(
+        subPath.indexOf('\0') < 0, "subPath must not contain null characters");

Review Comment:
   [Question] With the blanket backslash ban gone, the normalized-ancestor 
check is now the only defense - which is right server-side, but the accepted 
sub path is still echoed back verbatim.
   
   On a POSIX server this is safe: `Path.WINDOWS` is false, so `\` is an 
ordinary character, `/dir\..\outside` is one literal segment, and the boundary 
check keeps it inside the storage location. The residual case is the consumer 
of the returned string. `getFileLocation` now returns, by design and asserted 
in this PR, `s3a://bucket/prefix/..\outside` 
(TestFilesetCatalogOperations.java:4245) and `s3a://bucket/prefix/\..\outside` 
(:4246). Any client that treats `\` as a separator - a Hadoop `Path` built on a 
Windows JVM, where `normalizePath` rewrites `\` to `/` - collapses those to 
`s3a://bucket/outside`, i.e. outside the location the server just validated. 
GVFS reaches the same code through `BaseGVFSOperations#getActualFilePath` 
(clients/filesystem-hadoop3/src/main/java/org/apache/gravitino/filesystem/hadoop/BaseGVFSOperations.java:526),
 which passes the sub path through unchanged.
   
   The same hole exists for a segment that another layer trims, e.g. `/dir/.. 
/outside`: `.. ` is not equal to `..`, so the segment loop at line 1803 lets it 
through and the boundary check sees a literal directory name.
   
   Two questions, either answer is fine by me:
   1. Is the Windows-client case simply out of scope? If so it is worth one 
sentence in the PR's user-facing-change section, since the earlier discussion 
concluded the opposite way.
   2. If you want to keep it closed without losing literal names, a narrower 
guard is possible: reject a sub path that has a `..` segment after splitting on 
both `/` and `\`. `dir\name.txt` and `file\name.txt` stay addressable (the 
cases this commit restored), while only the traversal-shaped `..\` spellings 
are refused.
   
   Verified by: read the current check at 
FilesetCatalogOperations.java:1799-1806 and the boundary walk in 
`isWithinStorageLocation`/`normalizeLocationPath` in this checkout; read the 
new expectations at TestFilesetCatalogOperations.java:4239-4248; read the GVFS 
sub-path derivation at the line cited above. Hadoop's `Path.normalizePath` only 
substitutes `\` when `Path.WINDOWS` is true, which the new test acknowledges 
with `Assumptions.assumeFalse(Path.WINDOWS, ...)` at 
TestFilesetCatalogOperations.java:2120.



-- 
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]

Reply via email to