Jul 29, 2026·6 min read·3 visits
A path traversal vulnerability in `activerecord-tenanted` allows attackers to read or write arbitrary files on the host system by manipulating ActiveStorage keys.
A directory traversal vulnerability exists in the `activerecord-tenanted` Ruby gem's local storage path resolution logic. Prior to version 0.7.0, the `path_for` method failed to sanitize input keys, allowing remote attackers to traverse directories and access arbitrary files on the host filesystem.
The activerecord-tenanted library is a Ruby gem designed to facilitate multi-tenancy configurations within applications using Ruby on Rails and ActiveRecord. Specifically, it provides mechanisms to segment data and physical storage resources based on the active tenant context. A critical component of this segmentation is the local disk storage coordination layer, which overrides default ActiveStorage behaviors to route asset storage into tenant-specific directory subfolders.
A security vulnerability, designated as GHSA-PMWX-RM49-XV39, exists in versions prior to 0.7.0. The vulnerability is classified as a Path Traversal (CWE-22) flaw. It resides in the custom implementation of the path_for method within ActiveRecord::Tenanted::Storage::DiskService.
Under vulnerable configurations, the path resolution routine does not sanitize, canonicalize, or validate key parameters containing directory traversal sequences. This allows unauthenticated remote attackers to step outside the bounds of the designated active storage root folder. The flaw enables unauthorized read or write operations on arbitrary files on the host system, subject to the permissions of the application process.
To understand the technical root cause, we must examine how the activerecord-tenanted gem overrides the path generation logic of Rails ActiveStorage. When local disk storage is used, the system invokes ActiveRecord::Tenanted::Storage::DiskService#path_for(key) to determine the absolute file path on disk.
The vulnerable implementation uses standard string manipulation and directory joining via File.join without verification of the resulting path. It checks if the requested blob key contains a forward slash to separate the tenant prefix from the file identifier. It then constructs the final filesystem path using the schema File.join(root, tenant, folder_for(key), key).
This design makes the fundamental assumption that both the tenant segment and the key segment are safe, well-formed directory components. However, File.join merely concatenates path components using system directory separators. It does not perform path canonicalization, nor does it collapse relative directory references such as double-dots.
Because the system fails to verify that the resolved physical path resides within the intended parent directory, any input containing traversal sequences escapes the storage boundaries. Attackers can provide keys containing sequence paths that logically point to sensitive system resources.
The vulnerable implementation of the path resolution method exhibits a clear lack of input validation. The code block below represents the state of the component prior to the release of version 0.7.0.
def path_for(key)
if ActiveRecord::Tenanted.connection_class && key.include?("/")
tenant, key = key.split("/", 2)
File.join(root, tenant, folder_for(key), key)
else
super
end
endBy contrast, the patch introduced in version 0.7.0 adds substantial defensive validation layers to prevent directory traversal. The corrected method utilizes strict prefix matching and segment analysis to validate path inputs before returning a resolved path.
def path_for(key)
return super unless ActiveRecord::Tenanted.connection_class && key.include?("/")
# Block keys containing relative directory traversal elements
if key.split("/").intersect?(%w[. ..])
raise ActiveStorage::InvalidKeyError, "key has path traversal segments"
end
tenant, key = key.split("/", 2)
# Check for blank path components
if tenant.blank? || key.blank?
raise ActiveStorage::InvalidKeyError, "key has a blank segment"
end
begin
# Resolve the physical, canonical absolute path
path = File.expand_path(File.join(root, tenant, folder_for(key), key))
rescue ArgumentError
raise ActiveStorage::InvalidKeyError, "key is an invalid string"
end
# Validate that the resolved path begins with the storage root
unless path.start_with?(File.expand_path(root) + "/")
raise ActiveStorage::InvalidKeyError, "key is outside of disk service root"
end
path
rescue Encoding::CompatibilityError
raise ActiveStorage::InvalidKeyError, "key has incompatible encoding"
endThe primary defense in the updated method is the validation that the absolute path, computed via File.expand_path, begins with the expanded disk service root path. The check path.start_with?(File.expand_path(root) + "/") ensures that the path cannot traverse outside of the designated storage root.
Exploitation of this vulnerability requires that an attacker have a mechanism to submit or influence the storage keys processed by the ActiveStorage subsystem. This is often possible in applications that accept user-provided file uploads, utilize direct uploads, or resolve assets based on client-controlled identifiers.
An attacker can exploit the vulnerability by supplying a crafted key parameter that incorporates directory traversal sequences. For instance, the sequence ../../../../etc/passwd would split into a tenant segment of .. and a key segment of ../../../etc/passwd.
When the application invokes DiskService#path_for with the crafted key, the system resolves the logical path to /etc/passwd. Depending on the operation performed (read or write), the application will either disclose sensitive system configurations or overwrite files on the host container.
The impact of this vulnerability depends heavily on the role of ActiveStorage in the target application. If the application exposes endpoints that retrieve files by their keys, the directory traversal allows arbitrary file reading, resulting in the disclosure of application source code, environment variables, database credentials, and system files.
If the application enables file uploads where the key can be controlled or influenced by the client, the attacker can write or overwrite arbitrary files. This capability could lead to remote code execution if the attacker overwrites application executables, configuration files, or uploads a web shell to a directory parsed by the web server.
The vulnerability has an estimated CVSS score of 9.1 (Critical) due to the low complexity of exploitation and the potential for complete loss of confidentiality and integrity. The risk is heightened because file-upload and retrieval logic often operates under unauthenticated sessions.
The definitive resolution for GHSA-PMWX-RM49-XV39 is upgrading the activerecord-tenanted gem to version 0.7.0 or higher. This upgrade implements the necessary input validation checks and absolute path verification rules.
Because version 0.7.0 of the gem relies on ActiveStorage::InvalidKeyError, you must also upgrade your Ruby on Rails framework dependencies to version 8.1.2.1 or higher. This ensures that the custom disk service can raise the appropriate exception when encountering malicious keys.
# Update Gemfile
gem 'activerecord-tenanted', '>= 0.7.0'If upgrading is not immediately feasible, you can implement a temporary hotfix by creating an initializer that intercepts calls to DiskService#path_for and raises an error if the key contains dot-dot sequences or null bytes. Additionally, ensure the application process runs with the minimum necessary filesystem privileges.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
activerecord-tenanted Basecamp | < 0.7.0 | 0.7.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-22 |
| Attack Vector | Network (AV:N) |
| CVSS Score | 9.1 |
| Exploit Status | poc |
| KEV Status | Not Listed |
The software uses external input to construct a pathname that is intended to identify a file or directory that is located under a restricted parent directory, but the software does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
A critical security flaw (CVE-2026-54680) in the Kubernetes Logging Operator allows authenticated attackers with namespace-level access to craft malicious Custom Resources that inject arbitrary configuration directives into the downstream Fluentd logging aggregator, resulting in unauthenticated remote code execution (RCE) in the context of the aggregator pod.
A protection mechanism failure in the netfoil DNS proxy prior to version 0.4.0 causes blocked domains to resolve to null IP addresses (0.0.0.0 or ::) with a NOERROR status instead of NXDOMAIN. On Linux systems, connections to these addresses are routed to the loopback interface (localhost), allowing local processes to intercept sensitive HTTP traffic, including authorization headers and session cookies, intended for the blocked domains.
An uncontrolled resource consumption vulnerability in the OpenTelemetry Javaagent RMI context propagation mechanism allows remote unauthenticated attackers to cause a Denial of Service (DoS) via heap memory exhaustion.
A sensitive data exposure vulnerability (CWE-532) exists in OpenTelemetry Java Instrumentation prior to version 2.28.0. The JDBC auto-instrumentation component's SQL statement sanitizer contains lexer flaws that prevent the correct redaction of administrative passwords under specific conditions, such as when double-quotes represent identifiers or during multi-statement executions separated by semicolons.
CVE-2026-54705 is a critical DOM-based Cross-Site Scripting (XSS) vulnerability affecting the MathLive library prior to version 0.110.0. The flaw stems from a lack of proper character escaping in the rendering path for LaTeX text-mode commands such as \text{} and \mbox{}, enabling unauthenticated attackers to execute arbitrary JavaScript in the victim's browser.
A critical authorization bypass vulnerability was identified in the ZITADEL identity management platform, tracked as CVE-2026-54693 (GHSA-jq8w-8q2f-ffm9). Due to incorrect privilege validation in self-service profile modification endpoints, standard authenticated users could obtain plaintext verification codes during email or phone number updates. This allows attackers to claim and verify arbitrary email addresses or phone numbers without controlling the underlying contact methods.