Oct 28, 2026·10 min read·2 visits
FileBrowser Quantum fails to validate canonical paths after resolving symbolic links during file read operations. An authenticated user or an anonymous user accessing a public share can exploit an in-scope symbolic link to read arbitrary files from the underlying server filesystem. Upgrading to version 1.5.2-stable or 2.0.1-beta resolves this vulnerability by enforcing strict path containment checks and scoped root file descriptors.
FileBrowser Quantum prior to versions 1.5.2-stable and 2.0.1-beta contains an improper link resolution vulnerability (CWE-59) in its file indexing and retrieval subsystems. When processing file read, download, preview, media streaming, OnlyOffice integration, or public share requests, the application resolves symbolic links via Go's filepath.EvalSymlinks without verifying that the canonical target path remains within the configured source root and the user's assigned access scope. Consequently, authenticated users with read access or unauthenticated holders of public share links can read arbitrary server-accessible files outside their designated directory boundaries.
FileBrowser Quantum (gtsteffaniak/filebrowser) is a self-hosted, web-based file management application written in Go. It allows administrators to expose host filesystem directories—referred to as "Sources"—to users through a web interface and REST API. To support multi-tenancy and access isolation, administrators configure user-specific directory prefixes, known as "Scopes," which restrict each user's view and file operations to a designated subdirectory within a source root.
In versions prior to 1.5.2-stable and 2.0.1-beta, the file resolution engine located in backend/pkg/indexing/indexingFiles.go (and backend/indexing/indexingFiles.go in the v1.5 branch) improperly handles symbolic links when FollowSymlinks is enabled. When the application processes a request to index, preview, stream, or download a file, it resolves the target path using Go's filepath.EvalSymlinks via the GetRealPath method. However, the application fails to verify whether the resulting canonical filesystem path remains contained within either the global source root or the requesting user's authorized scope.
This weakness is classified under CWE-59 (Improper Link Resolution Before File Access) and CWE-22 (Improper Limitation of a Pathname to a Restricted Directory). Because the boundary validation occurs only on the unresolved relative path prior to symlink evaluation, any symbolic link situated within an accessible directory can reference targets anywhere on the host filesystem. The application subsequently opens and serves the resolved file path using the privileges of the FileBrowser Quantum server process.
The attack surface encompasses multiple HTTP handlers across both authenticated and unauthenticated workflows. Authenticated users with download permissions can exploit the flaw via standard file download (/api/resources), file preview, media streaming (/api/media/stream), and OnlyOffice document rendering endpoints. Furthermore, if an administrator or user creates a public share link (/public/api/resources or /public/api/media/stream) for a directory containing an outbound symbolic link, unauthenticated anonymous users holding the share link can read arbitrary files outside the shared directory boundary.
The root cause of CVE-2026-116682 lies in the separation between logical path normalization and physical symlink evaluation within the indexing subsystem. FileBrowser Quantum maintains a virtualized index path for each source root (idx.Path). When a user is assigned a scope (for example, /user1 inside /srv/storage), the application enforces access control by checking that the requested logical path begins with the user's scope prefix before passing the relative path to the indexing package.
Once the request passes the initial logical prefix check, the HTTP handlers invoke idx.GetRealPath(relativePath...) or idx.resolvePathContext(indexPath, followSymlinks) to obtain the physical host path for file I/O operations. In vulnerable releases, GetRealPath joins the source root (idx.Path) with the relative path segments and calls iteminfo.ResolveSymlinks(absolutePath), which wraps filepath.EvalSymlinks. This function traverses all symbolic links in the path and returns the final canonical target on the host OS.
Crucially, neither GetRealPath nor resolvePathContext receives the caller's userScope (or bound index path) as a parameter, nor do they validate the output of filepath.EvalSymlinks against idx.Path. If a symbolic link at /srv/storage/user1/link points to /etc/passwd or to a sibling user's directory /srv/storage/user2/private.txt, EvalSymlinks returns the external path without error. Furthermore, GetRealPath caches this unvalidated mapping in a global RealPathCache keyed solely by the joined input path, meaning subsequent lookups bypass resolution checks entirely.
When control returns to the HTTP layer—such as rawFilesHandler or ServeSingleFile in backend/http/download.go—the handler immediately passes the resolved realPath string to os.Open(realPath) and streams the resulting file descriptor via http.ServeContent. Because os.Open operates directly on the host filesystem namespace without root containment, the server reads and transmits the external file.
Examining the vulnerable implementation in backend/pkg/indexing/indexingFiles.go alongside the patches introduced in commits 4126edeb07a015c3809a89249b283e07a4e50c38 (v1.5.2-stable) and 3c8c02414eefd3351ffe437fa39ff6eb150ffa67 (v2.0.1-beta) illustrates the exact mechanics of the flaw and its remediation.
In the vulnerable codebase, resolvePathContext and GetRealPath resolved symbolic links unconditionally:
// Vulnerable implementation in backend/pkg/indexing/indexingFiles.go
func (idx *Index) resolvePathContext(indexPath string, followSymlinks bool) (*PathContext, error) {
realPath := filepath.Join(idx.Path, indexPath)
fileInfo, err := os.Lstat(realPath)
if err != nil {
return nil, err
}
isSymlink := fileInfo.Mode()&os.ModeSymlink != 0
if isSymlink && followSymlinks {
// FLAW: Evaluates symlink target anywhere on the OS without boundary checks
realPath, err = filepath.EvalSymlinks(realPath)
if err != nil {
return nil, err
}
}
// Continues processing using out-of-bounds realPath...
}
func (idx *Index) GetRealPath(relativePath ...string) (string, bool, error) {
combined := append([]string{idx.Path}, relativePath...)
joinedPath := filepath.Join(combined...)
// ...
// FLAW: Resolves symlinks and caches result globally without verifying scope containment
realPath, isDir, err := iteminfo.ResolveSymlinks(absolutePath)
if err == nil {
RealPathCache.Set(joinedPath, realPath)
IsDirCache.Set(joinedPath+":isdir", isDir)
}
return realPath, isDir, err
}To remediate this issue, the maintainers introduced explicit boundary calculation and validation helpers in indexingFiles.go. Specifically, filesystemBound computes the canonical path of both the source root (/) and the active scope prefix (boundIndexPath), while ensureResolvedContained verifies that the target path resides within both boundaries using iteminfo.PathWithinRoot:
// Patched implementation in backend/pkg/indexing/indexingFiles.go
func (idx *Index) filesystemBound(boundIndexPath string) (string, error) {
bound := boundIndexPath
if bound == "" {
bound = "/"
}
fsPath := idx.Path
if bound != "/" {
fsPath = utils.JoinUnderSourceRoot(idx.Path, bound)
}
abs, err := filepath.Abs(fsPath)
if err != nil {
return "", err
}
return filepath.EvalSymlinks(abs)
}
func (idx *Index) ensureResolvedContained(realPath, boundIndexPath string) error {
// 1. Verify containment within the global source root
sourceRoot, err := idx.filesystemBound("/")
if err != nil {
return err
}
if err := iteminfo.PathWithinRoot(sourceRoot, realPath); err != nil {
return errors.ErrPathEscapesScope
}
// 2. Verify containment within the user or share scope prefix
bound := boundIndexPath
if bound == "" {
bound = "/"
}
if bound != "/" {
scopeRoot, err := idx.filesystemBound(bound)
if err != nil {
return err
}
if err := iteminfo.PathWithinRoot(scopeRoot, realPath); err != nil {
return errors.ErrPathEscapesScope
}
}
return nil
}Additionally, the patch replaces un-scoped GetRealPath calls in HTTP handlers with GetRealPathScoped(boundIndexPath string, relativePath ...string) and partitions the cache key (fmt.Sprintf("realpath|scoped=%t|bound=%q|path=%q", enforceScope, bound, joinedPath)) so that cached resolutions from broader scopes cannot poison narrower scopes.
> [!NOTE]
> Fix Completeness & TOCTOU Hardening: Checking a path with EvalSymlinks followed by a separate os.Open call can still leave a narrow Time-of-Check to Time-of-Use (TOCTOU) window if an attacker can rapidly swap a file with a symlink during concurrent execution. To address this for regular within-scope paths, the patch also introduces iteminfo.OpenContained, which utilizes Go's os.OpenRoot(rootDir) API to perform kernel-enforced, traversal-resistant file opens relative to the scoped root directory. For legitimately resolved symlinks within scope, OpenScopedPath re-verifies containment immediately prior to descriptor creation. This multi-layered approach effectively neutralizes both direct symlink escapes and cross-scope cache poisoning.
Exploiting CVE-2026-116682 requires two conditions: an attacker must have read or download access to a directory served by FileBrowser Quantum (either via an authenticated user account or an unauthenticated public share link), and that directory must contain a symbolic link pointing to a target outside the assigned scope or source root.
Consider a multi-user environment where the server exposes /srv as a source root. User 1 is restricted to the scope /user1 (mapping to /srv/user1), while User 2 is restricted to /otheruser (mapping to /srv/otheruser). Suppose the directory /srv/user1 contains symbolic links referencing files outside /srv/user1, as modeled in the project's regression test suite (backend/pkg/indexing/symlink_containment_test.go):
/srv/
user1/ <-- User 1 assigned scope (/user1)
allowed.txt <-- Regular file inside scope
secret_link <-- Symlink -> /secret-outside.txt (Outside source root)
escape_dir <-- Symlink -> /srv/otheruser/ (Inside source root, outside user scope)
otheruser/ <-- User 2 private directory
leaked.txt
/secret-outside.txt <-- Host file outside /srv source mountTo read the host file /secret-outside.txt, an authenticated attacker logged in as User 1 sends a standard HTTP GET request to the /api/resources download endpoint referencing secret_link:
GET /api/resources?source=srv&path=/secret_link HTTP/1.1
Host: filebrowser.example.com
Cookie: filebrowser_quantum_jwt=<VALID_USER1_JWT>If /srv/user1 has been shared via a public link with hash <SHARE_HASH>, an unauthenticated attacker can achieve the exact same result without any credentials by querying the public share API:
GET /public/api/resources?hash=<SHARE_HASH>&path=/secret_link HTTP/1.1
Host: filebrowser.example.comOn vulnerable versions (< 1.5.2-stable and < 2.0.1-beta), the server resolves /srv/user1/secret_link to /secret-outside.txt, opens the file descriptor, and responds with 200 OK along with the raw contents of /secret-outside.txt. Similarly, requesting path=/escape_dir/leaked.txt traverses the directory symlink into /srv/otheruser/leaked.txt, bypassing inter-user tenant isolation. On patched versions, ensureResolvedContained detects that the target falls outside /srv/user1, raises errors.ErrPathEscapesScope, and the HTTP layer terminates the request with 403 Forbidden.
CVE-2026-116682 carries a CVSS v3.1 base score of 8.6 (High) with the vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N. The Scope: Changed (S:C) metric reflects that the vulnerable component (the FileBrowser Quantum web application sandbox) enforces a logical authorization boundary, while successful exploitation allows access to resources managed by a separate authority—namely, the underlying host operating system filesystem and other tenant scopes.
The primary security impact is a high-severity loss of confidentiality (C:H). An attacker who can trigger symlink resolution can read any file on the server that the FileBrowser Quantum OS process has permissions to read. When deployed directly on a Linux host or inside a privileged container, readable files typically include /etc/passwd, application configuration files, TLS private keys, environment variables (/proc/self/environ), SSH keys, and the FileBrowser Quantum database itself (which stores password hashes, API tokens, and share configurations).
In multi-tenant deployments where multiple users share a common backend storage server separated by FileBrowser Quantum scopes, the vulnerability completely undermines tenant isolation. A low-privileged user in one scope can read confidential documents, source code, or media belonging to any other user on the same host, even if those files reside in separate sources or sibling directories.
While the vulnerability is strictly limited to file read flows (I:N/A:N) and does not directly permit arbitrary file modification or deletion outside the scope, reading sensitive credentials, JWT signing keys, or database files from the host filesystem frequently provides attackers with the material required to escalate privileges to full administrative control over the application.
Administrators and organizations running FileBrowser Quantum should upgrade immediately to the patched releases that enforce scope-bounded symlink resolution and root-contained file opening:
1.5.2-stable or later.2.0.1-beta or later.In the patched versions, all file read, preview, media stream, OnlyOffice, and public share handlers pass the caller's effective scope to GetRealPathScoped and OpenScopedPath. Any symbolic link whose canonical target resolves outside the source root or outside the active user/share scope is rejected with errors.ErrPathEscapesScope (403 Forbidden).
If an immediate software upgrade is not operationally feasible, administrators should implement defense-in-depth mitigations at the filesystem and deployment layers:
..) out of user subdirectories:
find /path/to/storage/root -type l -exec ls -ld {} +chroot / mount namespace where only the intended data directory is mounted. Ensure that sensitive host paths (/etc, /root, /home, and application configuration/database directories) reside on separate mounts inaccessible to the container runtime.CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
FileBrowser Quantum (v1.x series) gtsteffaniak | < 1.5.2-stable | 1.5.2-stable |
FileBrowser Quantum (v2.x beta series) gtsteffaniak | >= 2.0.0-beta, < 2.0.1-beta | 2.0.1-beta |
| Attribute | Detail |
|---|---|
| CVE ID | CVE-2026-116682 (GHSA-mgqf-5mf5-prfj) |
| Weakness Class | CWE-59 (Improper Link Resolution) / CWE-22 (Path Traversal) |
| CVSS v3.1 Score | 8.6 High (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N) |
| Attack Vector | Network (HTTP/HTTPS via authenticated user or anonymous public share) |
| Security Impact | Arbitrary File Read outside user scope and server source root |
| Affected Component | backend/pkg/indexing/indexingFiles.go (GetRealPath & resolvePathContext) |
| Patched Versions | 1.5.2-stable and 2.0.1-beta |
| CISA KEV Status | Not Listed |
The software attempts to resolve a file reference into an underlying object, such as a file or directory, by following a symbolic link, without properly verifying that the target of the link stays within the intended control sphere or directory boundary.
CVE-2026-87018 (GHSA-qrxm-fqhp-jwvm) is a high-severity Denial of Service (DoS) vulnerability in undici, the default HTTP/1.1 and HTTP/2 client for Node.js that backs the global fetch() implementation. The flaw stems from reachable assertions in undici's socket and session error handlers, which assume that TLS hostname verification errors (ERR_TLS_CERT_ALTNAME_INVALID) cannot occur after the initial connection establishment. By initiating a post-handshake TLS 1.2 renegotiation and presenting a certificate with a mismatched Subject Alternative Name (SAN), a remote HTTPS server can trigger this assertion and terminate the entire Node.js runtime process via an uncaught AssertionError.
CVE-2026-116676 (GHSA-rjh2-4xgw-p2hj) is an OS command injection vulnerability (CWE-78) in Atlantis, an open-source Go application that automates Terraform pull request workflows via webhooks. From version 0.4.0 prior to version 0.47.1, Atlantis constructed Terraform and Conftest commands by joining argument slices into a single space-separated string and passing them to a shell interpreter (`sh -c`). Because user-controlled workspace names from `atlantis.yaml`, pull request comments, HCL configurations, and API requests were insufficiently validated against shell metacharacters, an authenticated contributor eligible to trigger a Terraform plan can execute arbitrary commands under the Atlantis service account.
CVE-2026-116679 (GHSA-5cp7-fp4j-wwcv) is an incorrect authorization vulnerability (CWE-863) in m1k1o/neko, a self-hosted virtual browser running inside Docker via WebRTC. In versions from 3.0.0 prior to 3.1.5, a boolean logic inversion in the LegacyIsHost() method within server/internal/session/session.go causes the server to unconditionally grant host input privileges over the legacy WebRTC data channel when implicit hosting is enabled. Authenticated non-privileged room members can exploit this flaw to bypass room control locks and profile restrictions, allowing unauthorized injection of mouse and keyboard events into the shared virtual browser session.
CVE-2026-116688 (GHSA-rh54-vffm-5fvp) is a high-severity Cross-Site Scripting (XSS) vulnerability in the CKEditor 5 engine package (`@ckeditor/ckeditor5-engine` and the umbrella `ckeditor5` package). The flaw originates from an upstream prototype pollution vulnerability in the `es-toolkit` dependency (versions prior to 1.52.0) used to parse and process inline HTML `style` attributes. When processing crafted CSS style declarations, the engine permits modification of `Object.prototype`, which alters downstream DOM conversion and sanitization checks to achieve arbitrary JavaScript execution in the browser context.
CVE-2026-116687 is a Cross-Site Scripting (XSS) vulnerability in the @ckeditor/ckeditor5-engine package of CKEditor 5. When the General HTML Support (GHS) plugin is configured to permit embedded container elements such as <object>, <embed>, or <iframe>, insufficient validation of data: URIs and incomplete stripping of ASCII C0 control characters allow attackers to execute arbitrary JavaScript within an isolated null-origin browser context.
CVE-2026-116686 is a Server-Side Request Forgery (SSRF) and Denial of Service (DoS) vulnerability in the official Model Context Protocol (MCP) Python SDK (mcp package on PyPI). When an MCP client validates structured tool execution results against a server-provided outputSchema, the SDK invokes the Python jsonschema library with its default URI resolution configuration. An untrusted or compromised MCP server can supply an outputSchema containing external $ref directives (using http://, https://, or file:// schemes), forcing the client process to synchronously fetch internal network resources, read local files, or block the main event loop indefinitely.