CVE-2026-1776
Description
Camaleon CMS versions 2.4.5.0 through 2.9.0, prior to commit f54a77e, contain a path traversal vulnerability in the AWS S3 uploader implementation that allows authenticated users to read arbitrary files from the web server’s filesystem. The issue occurs in the download_private_file functionality when the application is configured to use the CamaleonCmsAwsUploader backend. Unlike the local uploader implementation, the AWS uploader does not validate file paths with valid_folder_path?, allowing directory traversal sequences to be supplied via the file parameter. As a result, any authenticated user, including low-privileged registered users, can access sensitive files such as /etc/passwd. This issue represents a bypass of the incomplete fix for CVE-2024-46987 and affects deployments using the AWS S3 storage backend.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
camaleon_cmsRubyGems | >= 2.4.5.0, <= 2.9.1 | — |
Affected products
1Patches
1f54a77e2a7beFix path traversal in CamaleonCmsAwsUploader and add regression coverage
3 files changed · +123 −1
app/uploaders/camaleon_cms_aws_uploader.rb+8 −1 modified@@ -37,6 +37,8 @@ def objects(prefix = '/', sort = 'created_at') end def fetch_file(file_name) + return { error: 'Invalid file path' } unless valid_folder_path?(file_name) + return file_name if file_exists?(file_name) return file_name if bucket.object(file_name).download_file(file_name) && file_exists?(file_name) @@ -84,8 +86,9 @@ def file_parse(s3_file) # - same_name: false => avoid to overwrite an existent file with same key and search for an available key # - is_thumb: true => if this file is a thumbnail of an uploaded file def add_file(uploaded_io_or_file_path, key, args = {}) + return { error: 'Invalid file path' } unless valid_folder_path?(key) + args = { same_name: false, is_thumb: false }.merge(args) - res = nil key = "#{@aws_settings['inner_folder']}/#{key}" if @aws_settings['inner_folder'].present? && !args[:is_thumb] key = key.cama_fix_media_key key = search_new_key(key) unless args[:same_name] @@ -117,6 +120,8 @@ def add_folder(key) # delete a folder in AWS with :key def delete_folder(key) + return { error: 'Invalid folder path' } unless valid_folder_path?(key) + key = "#{@aws_settings['inner_folder']}/#{key}" if @aws_settings['inner_folder'].present? key = key.cama_fix_media_key bucket.objects(prefix: key.slice(1..-1) << '/').delete @@ -125,6 +130,8 @@ def delete_folder(key) # delete a file in AWS with :key def delete_file(key) + return { error: 'Invalid file path' } unless valid_folder_path?(key) + key = "#{@aws_settings['inner_folder']}/#{key}" if @aws_settings['inner_folder'].present? key = key.cama_fix_media_key begin
.github/copilot-instructions.md+1 −0 added@@ -0,0 +1 @@ +When running rspec tests, please ensure that the RAILS_ENV environment variable is always set to test. The command should be executed as RAILS_ENV=test bundle exec rspec.
spec/uploaders/aws_uploader_spec.rb+114 −0 added@@ -0,0 +1,114 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe CamaleonCmsAwsUploader do + init_site + + let(:current_site) { Cama::Site.first.decorate } + let(:hook_instance) { instance_double('UploaderHookInstance', hooks_run: nil) } + let(:uploader) { described_class.new({ current_site: current_site, aws_settings: {} }, hook_instance) } + let(:bucket) { instance_double('Aws::S3::Bucket') } + + before { allow(uploader).to receive(:bucket).and_return(bucket) } + + context 'with an invalid path containing path traversal characters' do + describe '#add_file' do + it 'returns an error' do + expect(bucket).not_to receive(:object) + + expect(uploader.add_file('/tmp/test.png', '../tmp/test.png')).to eql(error: 'Invalid file path') + end + end + + describe '#delete_folder' do + it 'returns an error' do + expect(bucket).not_to receive(:objects) + + expect(uploader.delete_folder('../tmp')).to eql(error: 'Invalid folder path') + end + end + + describe '#delete_file' do + it 'returns an error' do + expect(bucket).not_to receive(:object) + expect(hook_instance).not_to receive(:hooks_run) + + expect(uploader.delete_file('../tmp/test.png')).to eql(error: 'Invalid file path') + end + end + end + + context 'with an invalid URI-like path' do + describe '#add_file' do + it 'returns an error' do + expect(bucket).not_to receive(:object) + + expect(uploader.add_file('/tmp/test.png', 'file:///tmp/test.png')).to eql(error: 'Invalid file path') + end + end + + describe '#delete_folder' do + it 'returns an error' do + expect(bucket).not_to receive(:objects) + + expect(uploader.delete_folder('s3://bucket/folder')).to eql(error: 'Invalid folder path') + end + end + + describe '#delete_file' do + it 'returns an error' do + expect(bucket).not_to receive(:object) + expect(hook_instance).not_to receive(:hooks_run) + + expect(uploader.delete_file('https://example.com/file.txt')).to eql(error: 'Invalid file path') + end + end + end + + context 'with a valid file path' do + describe '#add_file' do + let(:s3_file) { instance_double('Aws::S3::Object') } + let(:parsed_file) do + { + 'name' => 'test.png', + 'folder_path' => '/safe', + 'url' => 'https://cdn.example.com/safe/test.png', + 'is_folder' => false, + 'file_size' => 123.45, + 'thumb' => '/safe/thumb/test-png.png', + 'file_type' => 'image', + 'created_at' => '2026-03-09T00:00:00Z', + 'dimension' => '100x100', + 'key' => '/safe/test.png' + } + end + + before do + allow(bucket).to receive(:object).and_return(s3_file) + allow(s3_file).to receive(:upload_file).and_return(true) + allow(uploader).to receive(:search_new_key).and_return('/safe/test.png') + allow(uploader).to receive(:file_parse).with(s3_file).and_return(parsed_file) + allow(uploader).to receive(:cache_item).with(parsed_file).and_return(parsed_file) + end + + it 'uploads the file and returns cached metadata' do + file_path = "#{CAMALEON_CMS_ROOT}/spec/support/fixtures/rails.png" + expect(hook_instance).to receive(:hooks_run).with( + 'uploader_aws_before_upload', + hash_including( + file: file_path, key: '/safe/test.png', args: hash_including(same_name: false, is_thumb: false) + ) + ) + + result = uploader.add_file(file_path, 'safe/test.png') + + expect(bucket).to have_received(:object).with('safe/test.png') + expect(s3_file).to have_received(:upload_file).with( + file_path, { acl: 'public-read' } + ) + expect(result).to eql(parsed_file) + end + end + end +end
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
8- github.com/owen2345/camaleon-cms/commit/f54a77e2a7be601215ea1b396038c589a0cab9afnvdPatchWEB
- github.com/owen2345/camaleon-cms/pull/1127nvdIssue TrackingPatchWEB
- www.vulncheck.com/advisories/camaleon-cms-aws-uploader-authenticated-path-traversal-arbitrary-file-readnvdPatchThird Party AdvisoryWEB
- github.com/advisories/GHSA-jw5g-f64p-6x78ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-1776ghsaADVISORY
- camaleon.websiteghsaWEB
- camaleon.websitenvdProduct
- github.com/rubysec/ruby-advisory-db/blob/master/gems/camaleon_cms/CVE-2026-1776.ymlghsaWEB
News mentions
0No linked articles in our index yet.