Andrea Cosentino created CAMEL-25886:
----------------------------------------
Summary: camel-ftp/mina-sftp - resolve listed symbolic links for
jailStartingDirectory
Key: CAMEL-25886
URL: https://issues.apache.org/jira/browse/CAMEL-25886
Project: Camel
Issue Type: Improvement
Components: camel-ftp, camel-mina-sftp
Reporter: Andrea Cosentino
Assignee: Andrea Cosentino
Fix For: 4.24.0, 4.22.3, 4.18.6
The SFTP consumers ({{sftp}} in camel-ftp and {{mina-sftp}}) apply
{{jailStartingDirectory}} to the path built from each listed name only:
{{RemoteFileConsumer.isWithinStartingDirectory()}} (camel-ftp-common) compacts
that path and compares it with the starting directory as text. The 4.23 upgrade
guide note of CAMEL-25412 states that the remote file consumers keep this
lexical check. A listed entry that the server reports as a symbolic link is
therefore handled like any other entry:
* {{SftpRemoteFile}} has no accessor for the link flag of the listing
attributes, so {{SftpRemoteFileJCraft}} ({{ChannelSftp.LsEntry}}) and
{{MinaSftpRemoteFile}} ({{SftpClient.DirEntry}}) do not expose it.
* {{AbstractSftpConsumer.doPollDirectory()}} (camel-ftp-common, shared by both
consumers) treats every entry that is not a directory as a file.
* The retrieval ({{ChannelSftp.get()}} in {{SftpOperations}},
{{SftpClient.read()}} in {{MinaSftpOperations}}) is resolved by the server,
which follows the link to its target wherever that is.
camel-file resolves symbolic links for {{jailStartingDirectory}} since
CAMEL-25412, and the ftp consumer ignores entries that the server lists as
links ({{FtpConsumer}}, "Ignoring unsupported remote file type"). This change
gives the SFTP consumers the same semantics as camel-file.
h3. Behaviour after the change
With {{jailStartingDirectory}} enabled (the default):
* A listed entry that the server reports as a symbolic link is resolved on the
server before it is accepted. If its target is outside the starting directory,
the entry is skipped and logged by {{GenericFileConsumer.isValidFile()}} as for
the existing lexical check (WARN the first time, DEBUG on later polls).
* A link whose target cannot be resolved (a dangling link) is skipped.
* Links that resolve inside the starting directory, and a starting directory
that is itself a link, work as before, so {{MinaSftpSymlinkIT}} and the
"Symbolic Links" section of the mina-sftp documentation stay valid.
With {{jailStartingDirectory=false}} the consumers keep following every link,
as today. No new option is added, so the endpoint metadata does not change.
h3. Proposed change
* {{SftpRemoteFile}} (camel-ftp-common): add {{default boolean
isSymbolicLink()}} returning {{false}}, so {{SftpRemoteFileSingle}} and other
implementations stay compatible. {{SftpRemoteFileJCraft}} returns
{{entry.getAttrs().isLink()}} and {{MinaSftpRemoteFile}} returns
{{entry.getAttributes().isSymbolicLink()}}.
* A small interface in camel-ftp-common that resolves a link on the server,
implemented by {{SftpOperations}} ({{ChannelSftp.realpath()}} and
{{readlink()}}) and {{MinaSftpOperations}} ({{SftpClient.canonicalPath()}} and
{{readLink()}}), instead of a new method on the public {{RemoteFileOperations}}
interface, which {{FtpOperations}} implements too. It uses realpath. Where the
server's realpath does not resolve links (it returns the link's own path, as
Apache MINA SSHD does), it resolves the target itself with lstat and readlink,
component by component, relative to the link's directory and up to a fixed
number of links. The starting directory is resolved the same way, once per poll.
* {{AbstractSftpConsumer}}: check an entry flagged as a link right after the
existing lexical check, either through a protected overload of the
{{GenericFileConsumer.isWithinStartingDirectory()}} hook that also receives the
listed entry (defaulting to the String variant), or in {{AbstractSftpConsumer}}
itself.
* Only entries that the server reports as links are resolved, and no entry is
required to be a regular file. CAMEL-1247 removed an earlier check that skipped
the entries listed as links, because on Windows it skipped regular files too;
the current {{SftpATTRS.isLink()}} compares the whole file type field.
* camel-4.18.x has no camel-ftp-common: there {{SftpConsumer}} and
{{MinaSftpConsumer}} each have their own polling loop, so the change goes into
both, and the CAMEL-1247 comment in {{SftpConsumer}} is replaced.
h3. Documentation
* {{sftp-component.adoc}}: a "Symbolic links and the starting directory"
section, as in {{file-component.adoc}}.
* {{mina-sftp-component.adoc}}: amend "Symbolic Links": links resolving outside
the starting directory are skipped unless {{jailStartingDirectory=false}}.
* Regenerate the catalog copies of both pages.
* Upgrade guide notes on main, since links to files outside the starting
directory are no longer consumed: {{camel-4x-upgrade-guide-4_24.adoc}}, plus
the "Upgrading from 4.22.2 to 4.22.3" section of the 4_22 guide and the
"Upgrading from 4.18.5 to 4.18.6" section of the 4_18 guide. While there, add
the CAMEL-25412 notes that main's 4_22 and 4_18 guides do not have yet.
h3. Tests
* {{RemoteFileConsumerStartingDirectoryJailTest}} (camel-ftp): with stubbed
listing entries, a link resolving outside the starting directory is skipped and
a link resolving inside is accepted.
* New {{SftpConsumerJailStartingDirectorySymlinkIT}} (camel-ftp) and
{{MinaSftpConsumerJailStartingDirectorySymlinkIT}} (camel-mina-sftp): a link
resolving inside is consumed; a link resolving outside is skipped, and consumed
when {{jailStartingDirectory=false}}; a dangling link is skipped. The embedded
SFTP server (Apache MINA SSHD with its default settings) lists a link as a
link, as OpenSSH does, so the tests run against it unchanged.
* {{MinaSftpSymlinkIT}} stays green.
h3. Notes
* Whether a listing reports an entry as a link depends on the server. OpenSSH
and Apache MINA SSHD (with its default settings) list a link with its own
attributes, so the listing is enough there. A server that reports the target's
attributes in its listings hides the link; recognising such entries too would
need an LSTAT per listed entry, one more round trip per file and poll. The
proposal relies on the listing attributes, and the documentation says so.
* The check and the retrieval that follows are not atomic (an SFTP open follows
links on the server), so the permissions and link settings of the SFTP server
still matter.
* Backport to camel-4.22.x and camel-4.18.x, as for CAMEL-25412.
----
_Claude Code on behalf of [~acosentino]_
--
This message was sent by Atlassian Jira
(v8.20.10#820010)