253 Commits
Author SHA1 Message Date
Nicola Murino bca13cd448 vfs: skip listed names that address no entry
Listings leave out the entries naming the listed directory or its
parent, and the names holding a backslash, which no request can
address

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-09-28 20:11:13 +02:00
Nicola Murino 55e443d29e drop the dead nolint directives
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-09-09 12:37:54 +02:00
Nicola Murino 7e1e268862 transferchecks: fix flaky tests
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-09-07 21:18:37 +02:00
Nicola Murino ffeec76674 webdav: return the correct ETag after upload, or no ETag if unavailable
Fixes #2282

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-08-27 15:27:27 +02:00
Nicola Murino 48ccf188a0 say what a tolerated failure costs instead of calling it best effort
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-08-27 07:50:33 +02:00
Nicola Murino 407a390b61 test: wait for the connections to drain before removing the home dirs
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-08-26 23:36:25 +02:00
Nicola Murino 27d8298384 rename: check the entries of a renamed directory, or refuse the rename
A directory rename authorized the whole tree from a walk performed before the
move, so the file_patterns filters and the per-directory rename permissions
decided on the tree as the walk had read it rather than on the one the move
carried.

The walk no longer authorizes the move. A gate decides from the configuration
alone whether anything can tell the entries of the tree apart: a move that
changes which file_patterns filters govern the contents, or a per-directory
permission that restricts renaming defined at or below either tree. When
something can, the object stores check every entry at the moment they move it,
since they rename a directory by recursing into it, and the filesystems that
rename it in a single operation refuse it, because nothing enumerates what that
operation carries. The type of the source is read again immediately before the
move.

Granting rename_files and rename_dirs separately keeps the pre-move walk.

So renaming a folder into or out of a path where a different file_patterns
filter applies is now refused on the local filesystem, SFTP and HTTP, and
checked entry by entry on the object stores; the same holds for a per-directory
permission that restricts renaming inside either tree. Renames the walk used to
refuse fail with the same error as before. IsRenameAtomic is derived from the
same capability instead of matching the filesystem name.

Reported-by: Wade Sparks (@wsparks-vc)

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-08-26 23:21:19 +02:00
Nicola Murino a2ffc5e7da rename: remove SetPathPermissions
useless on rename, permissions are preserved

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-08-26 14:40:36 +02:00
Nicola Murino d2fc01a640 check transfers: use compare and swap
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-08-25 10:02:09 +02:00
Nicola Murino bde9ffde10 transferschecker: wait for async transfer registration in tests
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-08-25 09:52:48 +02:00
Nicola Murino 230ecb5e2b common: add V1AcceptIPv4InTCP6
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-08-24 22:13:33 +02:00
Nicola Murino 459cb2ba8f fix a race in test cases
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-08-24 21:42:24 +02:00
Nicola Murino b4698f1f4d dataprovider: refuse to delete referenced folders, roles and groups
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-08-24 18:27:56 +02:00
Nicola Murino d54965865e replace golangci-lint with staticcheck, errcheck and unparam
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-08-24 15:07:16 +02:00
Nicola Murino 5542293ab9 fix test cases on Windows
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-08-12 23:48:03 +02:00
Nicola Murino 4b93982fa8 common: do not apply the user transfer limits to the event actions
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-08-12 22:20:21 +02:00
Nicola Murino b4d870aa9a common: check the target write permission and quota on the server-side copy
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-08-12 22:12:17 +02:00
Nicola Murino de6bc600a3 common: report a hidden path as missing consistently
this is a best effort and outside the security boundaries

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-08-12 22:10:30 +02:00
Nicola Murino b7a2404f77 common: match the entries when a rename changes the filter scope
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-08-12 22:04:19 +02:00
Nicola Murino d389a3b048 common: apply the name filter wherever a directory is created
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-08-12 21:56:15 +02:00
Nicola Murino 03368b8d34 common: authorize a directory copy before it creates anything
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-08-12 21:52:15 +02:00
Nicola Murino 604664b5a1 common: evaluate the name filter on the source of copy, rename and symlink
The file pattern filter matches names, and the operations that read an item
evaluate it on the item itself. Three did not agree with that: a copy
evaluated it on the target only, a rename mapped every refusal to a
permission error and so discarded the deny policy configured on the filter,
and symlink creation honored the hide policy alone, letting the default one
through.

They now all evaluate the source and report the configured policy through
GetErrorForDeniedFile, as stat, download and delete already do, so the same
filter gives the same answer whichever operation reaches a name. The target
keeps reporting a permission error: it is a path being written, not an item
being read. isRenamePermitted returns an error and is renamed accordingly;
the recursive walk still reports a permission error, because the entries it
walks are the contents of the directory being renamed and a hidden one among
them must not make that directory look missing.

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-08-12 21:50:00 +02:00
Nicola Murino 236a9c7c68 vfs: various os.Root improvements
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-08-03 22:43:59 +02:00
Nicola Murino e3bc808686 common: compare the resolved copy paths within the same namespace
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-08-03 22:23:13 +02:00
Nicola Murino eca67e750a fixed some test cases
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-07-18 16:19:48 +02:00
Nicola Murino 6cce58233c virtual folders: add subpath support
A virtual folder mapping can now re-root the mount onto a sub-path of
the folder: the mount at the virtual path serves the folder starting
from the configured subpath, which stays invisible in the virtual tree.
The same folder can be mapped multiple times with distinct subpaths;
quota remains a property of the whole folder, so every mapping must use
the same limits.

Supported for local, encrypted, S3, GCS, Azure Blob and SFTP folders.

Fixes #2037

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-07-18 16:19:43 +02:00
Nicola Murino bafb7e7d6a modernize the codebase to current Go idioms
Apply the fixes suggested by the modernize analyzers enabled by
default in gopls v0.23: range over int, errors.AsType, min/max,
slices.Backward/Contains, maps.Copy, strings.Cut/CutPrefix/CutSuffix/
SplitSeq, fmt.Appendf, WaitGroup.Go and b.Loop in tests, new(expr)
instead of to.Ptr, removal of ineffective omitempty tags on nested
struct fields.

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-07-13 20:52:29 +02:00
Nicola Murino 7d7a6e6217 test: release the fs roots before removing the test home dirs
On Windows the home dir cannot be removed while a filesystem root is
open

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-07-06 18:48:38 +02:00
Nicola Murino 661d77f47b vfs: migrate OsFs/CryptFs to os.Root and remove temp_path
Confine all local filesystem access to an os.Root, with path resoultion
enforced by the underlying OS.

Behavior changes an upgrading deployment may notice:

- temp_path is removed and ignored if still configured. Its purpose
  was to stage files (atomic uploads, scratch and streaming data)
  outside the user's storage and rename them into place, and that is
  fundamentally incompatible with the confinement: an os.Root cannot
  rename a file in from outside the root, so every staged upload would
  degrade to a full copy-then-delete even when temp_path is on the
  same filesystem, defeating the very atomicity the option existed to
  provide. It was not a great idea before this change either: a
  temp_path on a different device already turned the final rename into
  a silent full copy. Staged and scratch files (the atomic upload
  temporary, the CryptFs download pipe backing file) now live in the
  storage root itself, on the same filesystem as the data, so the
  final rename stays atomic.

- Cross-folder / cross-root renames are no longer atomic. A rename
  spanning the home and a local virtual folder, or two local folders,
  falls back to copy-then-delete: slower for large data even on the
  same device, and a midway failure can leave a partial tree at the
  destination. Symlinks and other non-regular entries inside a moved
  directory are skipped, not copied, and the rename still reports
  success. File mode bits are not preserved; regular files keep their
  modification time, directories do not.

- Absolute symlinks created outside SFTPGo, or by versions predating
  this change, are no longer traversable and Readlink rejects them with
  permission denied, even when the target is inside the root: os.Root
  refuses an absolute link target regardless of where it points.
  Symlinks created through SFTPGo are stored relative to the link
  directory and keep working.

- Execute-only home directories (e.g. mode 0111) are unusable:
  os.OpenRoot requires read permission, so every operation fails with
  permission denied until the directory is made readable, where
  previously only the listing failed. Since the root open is retried on
  demand, fixing the permissions (or creating a missing home
  externally) makes the same connection work again without
  reconnecting.

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-07-06 13:08:12 +02:00
Nicola Murino b163bab867 azure blob: include container in isSameResource check
Fixes #2254

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-06-29 15:03:31 +02:00
Nicola Murino 8f23f217b8 fix symlink test cases
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-06-20 22:03:33 +02:00
Nicola Murino c051a3f90c common: require create_symlinks on both the link and the target directory
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-06-20 21:03:44 +02:00
Nicola Murino fa9c047619 Add dedicated setting to control symlink creation
Symlink creation is now disabled by default

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-06-20 20:46:50 +02:00
Nicola Murino 91d2893264 fix overquota transfers check
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-06-04 08:14:52 +02:00
Nicola Murino 56bc955e0d shares: confine partial ZIP download entries to the share directory
The browsable share partial download validated ZIP entries with a raw
byte-prefix check, letting prefix-overlapping siblings (/share vs
/share2) escape the share root. Use a directory-boundary aware check;
harden the event manager twin and add a regression test.

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-05-30 12:57:39 +02:00
Nicola Murino 361ff35dcc Event Manager: neutralize CSV formula injection
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-05-23 16:48:22 +02:00
Nicola Murino 02e312f39c fs: enforce a configurable minimum entropy for data-encryption secrets
The CryptFs passphrase and the S3 SSE-C key are used directly as
data-at-rest encryption key material, yet they were accepted with no
strength check, so an operator could protect encrypted data with a
trivially guessable secret.

Add common.secret_min_entropy (default 80): when one of these
secrets is submitted in plain text it must pass a minimum-entropy
check before it is encrypted and stored. The check runs on
plain-text input, so secrets that are already stored keep working even
if the threshold is later raised. The estimate is a heuristic used to
reject obviously weak input, not a measure of cryptographic strength:
always supply key material generated by a CSPRNG.

Thanks to Vishal Shukla (@shukla304) for the report.

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-05-18 19:46:19 +02:00
Nicola Murino 4bf1090bd3 transfers: count per-host transfer limit per client IP, not per user
Fixes #2237

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-05-18 18:00:42 +02:00
Nicola Murino f32868d7f5 logs: add virtual path to transfer/command logs and event CSV columns
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-05-15 11:02:39 +02:00
Nicola Murino 2f092d1289 fix: prevent path traversal via edge-level path normalization
Moved path sanitization (backslash conversion and path cleaning) to
the SFTP/FTP handlers before VFS routing and permission checks.

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-03-07 12:03:59 +01:00
Nicola MurinoandJoel Studler 4c00f6061c reply to stat calls also for ongoing transfers on atomic storage backends
the check is performed only on the connection where the transfer is
initiated so it is inexpensive

Fixes #2162

Co-authored-by: Joel Studler <joel.studler@swisscom.com>
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-02-16 17:54:10 +01:00
mmtootmm 133d2692c4 Fix/ftp active connection closed (#2164)
ftpd: avoid fresh ftp connection being closed
2026-01-27 18:48:59 +01:00
Nicola Murino ed0c1a01ab micro optimization to the function keepConnectionAlive
we don't need a goroutine

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2026-01-21 18:30:46 +01:00
Nicola Murino ac3e59562d Enforce missing naming rule for actions and rules
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2025-12-07 10:35:13 +01:00
Nicola Murino 90685d8ef2 fix random failure in test cases
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2025-10-10 21:30:05 +02:00
Nicola Murino fa70ff35c4 Add debug log to investigate intermittent test failure in CI
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2025-10-08 19:22:35 +02:00
Nicola Murino 35525e22e9 remove rsync support
rsync was executed as an external command, which means we have no insight
into or control over what it actually does.
From a security perspective, this is far from ideal.

To be clear, there's nothing inherently wrong with rsync itself. However,
if we were to support it properly within SFTPGo, we would need to implement
the low-level protocol internally rather than relying on launching an external
process. This would ensure it works seamlessly with any storage backend,
just as SFTP does, for example.
We recommend using one of the many alternatives that rely on the SFTP
protocol, such as rclone

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2025-09-28 18:15:15 +02:00
Nicola Murino e892748ef4 system commands: recursively verify required permissions
If any permission is missing at any level, return a "Permission Denied"
error

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2025-09-28 09:36:19 +02:00
Nicola Murino cdaefbf04a Fix flaky test case
ensure the user filter is set on the rule so notification triggers
only when expected.

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2025-09-22 09:16:28 +02:00
Nicola Murino fe78974b47 remove data retention hook
use the EventManager instead

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
2025-07-14 18:22:32 +02:00