Croway commented on code in PR #29855:
URL: https://github.com/apache/camel/pull/29855#discussion_r4568597635


##########
components/camel-file/src/main/java/org/apache/camel/component/file/strategy/GenericFileProcessStrategySupport.java:
##########
@@ -127,6 +128,12 @@ public void 
setExclusiveReadLockStrategy(GenericFileExclusiveReadLockStrategy<T>
     }
 
     protected GenericFile<T> renameFile(GenericFileOperations<T> operations, 
GenericFile<T> from, GenericFile<T> to) {
+        if (operations instanceof FileOperations fileOperations) {
+            // check before the existing target is deleted, its parent 
directories are created and the file is renamed,
+            // as a symbolic link on the target path could otherwise move the 
file outside the starting directory
+            fileOperations.jailMoveToStartingDirectory(new 
File(to.getAbsoluteFilePath()));

Review Comment:
   **A refused commit move or `moveFailed` makes the file be processed again on 
every poll**
   
   This check runs in `commit` and `rollback`, after the route has already 
processed the file. When it throws, `GenericFileOnCompletion` only passes the 
error to the exception handler and removes the file from the in-progress 
repository. The file is still in the inbox, so the next poll picks it up again, 
with no limit.
   
   Reproduced against 28d47042 (`initialDelay=0&delay=100`, route 
`convertBodyTo(String)` → `mock:result`):
   
   | Setup | Deliveries of the same file within ~0.5 s |
   |---|---|
   | `inbox/.camel` is a link to a directory outside `inbox` | 5 (5 errors, 
file stays in `inbox`) |
   | `moveFailed=failed`, `inbox/failed` is a link outside, route throws | 5 |
   | No link (control) | 1 |
   
   The `moveFailed` case is the worst one, as users configure `moveFailed` 
precisely to stop a failing file from being retried.
   
   Any failed commit move already behaves like this, but this check fails on 
every poll, for good. Where the target can be evaluated up front, checking it 
when the file is listed or in `begin` and skipping the file (as 
`FileConsumer.isWithinStartingDirectory` does for a listed link that resolves 
outside) would avoid processing the file at all. Where it cannot (the 
expression depends on the exchange), the docs should at least say the file is 
consumed again on every poll.
   
   _Claude Code on behalf of Federico Mariani_



##########
docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc:
##########
@@ -77,6 +77,16 @@ at `DEBUG` level with the message only. Checkpoint failures 
triggered by batch s
 The webhook consumer still logs the body it validates at `DEBUG`, but no 
longer the signature from the `X-Signature` header, nor the signature it 
calculates.
 The `InvalidMessageSignatureException` of a failed check no longer carries the 
calculated signature: `getCalculatedSignature()` returns `null` and is 
deprecated, as is the constructor that takes it.
 
+=== camel-file - the consumer's move, preMove and moveFailed resolve symbolic 
links for jailStartingDirectory
+
+With `jailStartingDirectory` enabled (the default), the file consumer now 
resolves symbolic links on the target of
+`move`, `preMove` and `moveFailed`, including the default `.camel` folder, 
when that target is inside the starting
+directory. A target that resolves outside the starting directory through a 
link is not used: the move fails like any
+other failed move, so the file stays where it is and the error is passed to 
the exception handler. Before, the existing

Review Comment:
   **Behaviour change in patch releases for links set up on purpose, and the 
guide does not say the file is processed again**
   
   Some deployments keep processed files on another volume by linking `.camel` 
(or `done` with `move=done`). That link is set up by the operator, who is 
trusted under the security model. After upgrading to 4.18.6 / 4.22.3, every 
file consumed in such a setup fails its move and is processed again on every 
poll (see the comment on `GenericFileProcessStrategySupport`).
   
   Reproduced with `inbox/.camel` as a link to an `archive-volume` directory:
   - `jailStartingDirectory=false` (the code path before this PR, as the new 
check returns early): delivered once, file archived.
   - Default: delivered 5 times within ~0.5 s, never archived.
   
   "the file stays where it is" reads as harmless. I'd suggest saying 
explicitly that the file is consumed again on every poll until the link is 
removed or `jailStartingDirectory=false` is set, in the 4.18, 4.22 and 4.24 
guides.
   
   _Claude Code on behalf of Federico Mariani_



##########
components/camel-file/src/main/java/org/apache/camel/component/file/strategy/GenericFileProcessStrategySupport.java:
##########
@@ -127,6 +128,12 @@ public void 
setExclusiveReadLockStrategy(GenericFileExclusiveReadLockStrategy<T>
     }
 
     protected GenericFile<T> renameFile(GenericFileOperations<T> operations, 
GenericFile<T> from, GenericFile<T> to) {
+        if (operations instanceof FileOperations fileOperations) {

Review Comment:
   **Producer renames and deletes bypass this check (predates this PR, from 
CAMEL-25412, but relevant to where the check lives)**
   
   Because the check sits in the consumer strategy base class, the producer's 
own `deleteFile`/`renameFile` calls are not covered. Two cases reproduced 
against 28d47042, both changing files outside the starting directory without 
any error:
   
   1. **`moveExisting`**: 
`file:out?fileExist=Move&moveExisting=archive/${file:onlyname}`, `out/archive` 
is a link to `outside/`, and both `out/cfg.txt` and `outside/cfg.txt` exist. 
Writing `cfg.txt`: `existsFile`/`deleteFile` in 
`GenericFileDefaultMoveExistingFileStrategy` delete the real `outside/cfg.txt`, 
and the old `out/cfg.txt` is renamed onto it.
   2. **`tempFileName`**: `file:out?tempFileName=../tmp-${file:onlyname}` with 
`CamelFileName=sub/hello.txt`, `out/sub` is a link to `outside/`. The temp file 
`out/tmp-hello.txt` passes the check, then the eager delete and 
`operations.renameFile(tempTarget, target)` in `GenericFileProducer` replace 
`outside/hello.txt` with the new content. The same target without 
`tempFileName` is refused by `storeFile` ("Cannot write file: … as it resolves 
outside the starting directory").
   
   Neither is a regression from this PR, so a follow-up is fine. A single check 
in `FileOperations` (or a no-op hook on `GenericFileOperations` that 
`FileOperations` overrides) that runs before a target is deleted or renamed 
onto would cover these callers and the consumer strategies in one place, 
without the `instanceof` in the generic base class. In both producer paths the 
existing target is deleted before the rename, so the check has to run before 
the delete.
   
   _Claude Code on behalf of Federico Mariani_



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