SteNicholas commented on PR #3674:
URL: https://github.com/apache/celeborn/pull/3674#issuecomment-4627685842

   ## Review — Validate applicationId to prevent worker path traversal
   
   Good catch on the underlying issue, and the fix targets the right sink. The 
worker builds `<workingDir>/<appId>/<shuffleId>/<fileName>` in 
`StorageManager.createDiskFile`, which is reached at `ReserveSlots` time — and 
that path is now validated. A few things to address before merge.
   
   ### What works well
   - **The canonical-path backstop is done correctly**: `dir.getCanonicalPath + 
File.separator` with `startsWith` avoids the sibling-prefix false-negative 
(`/work` vs `/work-evil`), and moving `shuffleDir.mkdirs()` to *after* the 
check means a traversal directory is never created.
   - **Allowlist charset is the right approach** — rejecting anything outside 
`[A-Za-z0-9_-]` excludes `.`, `/`, `\`, and spaces, so `..` and path separators 
can't appear at all.
   - `DestroyWorkerSlots` correctly splits the shuffleKey first, then validates 
the extracted appId.
   - Solid positive/negative test coverage.
   
   ### Issues
   
   1. **Description doesn't match the diff** (please reconcile). The PR text 
says validateAppId is called "at `PushDataHandler.handleCore`, and the two 
checkAuth sites in `FetchHandler`," but the diff changes only 4 files — 
`Utils.scala`, `UtilsSuite.scala`, `Controller.scala`, `StorageManager.scala`. 
There are no changes to `PushDataHandler` or `FetchHandler`. Either those calls 
were dropped from the commit, or the description is stale.
   
      For what it's worth, the impact is limited: 
`FetchHandler`/`PushDataHandler` resolve files via 
`StorageManager.getFileInfo(shuffleKey, fileName)`, which is a 
`ConcurrentHashMap` lookup — it does **not** reconstruct a filesystem path from 
`appId`. So those handlers aren't independent traversal sinks, and the missing 
calls are defense-in-depth/consistency rather than a hole in the core fix. But 
the description should reflect what actually ships.
   
   2. **The regex accepts a trailing newline** (low severity, but a validator 
should be airtight). `^[A-Za-z0-9_-]+$` evaluated with `findFirstIn` matches 
`"validapp\n"`, because `$` (without `MULTILINE`) matches *before a final line 
terminator*, not strictly end-of-input. I verified the anchor semantics — 
Java's `java.util.regex` behaves the same:
      ```
      "validapp\n"  -> matches (not rejected)
      ```
      A lone trailing `\n` in a directory name isn't itself a traversal 
sequence, so real-world impact is small, but the clean fix is a full-region 
match:
      ```scala
      if (applicationId == null || 
!appIdPattern.pattern.matcher(applicationId).matches()) { ... }
      ```
      (`matches()` anchors the whole input; equivalently use `\A...\z`.) Worth 
adding `"app\n"` to the negative test cases.
   
   3. **Backstop is local-disk-only** (defense-in-depth gap). The 
canonical-path containment check is in the local-disk branch of 
`createDiskFile`, but the HDFS/S3/OSS branches also build 
`s"$appId/$shuffleId"` paths (e.g. `StorageManager.scala:1166`, `1171`) and 
rely solely on the upstream `validateAppId`. Given the Controller guard is the 
primary defense this is acceptable, but if the intent is layered defense, the 
DFS branches currently have none — at least worth a comment noting the 
backstop's scope.
   
   4. **Consider centralizing the check** (maintainability). Validation is 
currently sprinkled per-RPC-case, which is exactly why items 1 & 3 are easy to 
get wrong — a future handler can forget the call. Since every guarded site 
calls `checkAuth(context, applicationId)` right after, calling `validateAppId` 
*inside* `checkAuth` (so it runs even when auth is disabled) would cover all 
current and future entry points from one place.
   
   Nice, well-scoped security fix overall — the main blocker is item 1 (make 
the description and code agree), and item 2 is a quick hardening of the 
validator itself.
   


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