From 0542f3ac53688bac5e6aabef59a0508141f63527 Mon Sep 17 00:00:00 2001 From: Al Snow Date: Wed, 4 Feb 2026 10:49:03 -0500 Subject: [PATCH] GHSA SYNC: 2 brand new decideim-related advisories --- gems/decidim-core/CVE-2025-65017.yml | 132 +++++++++++++++++++++++++++ gems/decidim/CVE-2025-65017.yml | 132 +++++++++++++++++++++++++++ 2 files changed, 264 insertions(+) create mode 100644 gems/decidim-core/CVE-2025-65017.yml create mode 100644 gems/decidim/CVE-2025-65017.yml diff --git a/gems/decidim-core/CVE-2025-65017.yml b/gems/decidim-core/CVE-2025-65017.yml new file mode 100644 index 0000000000..81d4aacf2b --- /dev/null +++ b/gems/decidim-core/CVE-2025-65017.yml @@ -0,0 +1,132 @@ +--- +gem: decidim-core +cve: 2025-65017 +ghsa: 3cx6-j9j4-54mp +url: https://github.com/decidim/decidim/security/advisories/GHSA-3cx6-j9j4-54mp +title: Decidim's private data exports can lead to data leaks +date: 2026-02-03 +description: | + ### Impact + + Private data exports can lead to data leaks in cases where the UUID + generation causes collisions for the generated UUIDs. + + The bug was introduced by #13571 and affects Decidim versions 0.30.0 + or newer (currently 2025-09-23). + + This issue was discovered by running the following spec several + times in a row, as it can randomly fail due to this bug: + + ```bash + $ cd decidim-core + $ for i in {1..10}; do bundle exec rspec + spec/jobs/decidim/download_your_data_export_job_spec.rb + -e "deletes the" || break ; done + ``` + + Run the spec as many times as needed to hit a UUID that converts + to `0` through `.to_i`. + + The UUID to zero conversion does not cause a security issue but + the security issue is demonstrated with the following example. + + The following code regenerates the issue by assigning a predefined + UUID that will generate a collision (example assumes there are + already two existing users in the system): + + ```ruby + # Create the ZIP buffers to be stored + buffer1 = Zip::OutputStream.write_buffer do |out| + out.put_next_entry("admin.txt") + out.write "Hello, admin!" + end + buffer1.rewind + buffer2 = Zip::OutputStream.write_buffer do |out| + out.put_next_entry("user.txt") + out.write "Hello, user!" + end + buffer2.rewind + + # Create the private exports with a predefined IDs + user1 = Decidim::User.find(1) + export = user1.private_exports.build + export.id = "0210ae70-482b-4671-b758-35e13e0097a9" + export.export_type = "download_your_data" + export.file.attach(io: buffer1, filename: "foobar.zip", + content_type: "application/zip") + export.expires_at = Decidim.download_your_data_expiry_time.from_now + export.metadata = {} + export.save! + + user2 = Decidim::User.find(2) + export = user2.private_exports.build + export.id = "0210d2df-a0c7-40aa-ad97-2dae5083e3b8" + export.export_type = "download_your_data" + export.file.attach(io: buffer2, filename: "foobar.zip", + content_type: "application/zip") + export.expires_at = Decidim.download_your_data_expiry_time.from_now + export.metadata = {} + export.save! + ``` + + Expect to see an error in the situation. + + Now, login as user with ID 1, go to `/download_your_data`, click + "Download file" from the export and expect to see the data that + should be attached to user with ID 2. This is an artificially + replicated situation with the predefined UUIDs but it can easily + happen in real situations. + + The reason for the test case failure can be replicated in case + you change the export ID to + `export.id = "e9540f96-9e3d-4abe-8c2a-6c338d85a684"`. + This would return `0` through `.to_s` + + After attaching that ID, you can test if the file is available + for the export: + + ```ruby + user.private_exports.last.file.attached? + => false + user.private_exports.last.file.blob + => nil + ``` + + Note that this fails with such UUID as shown in the example and + could easily lead to collisions in case the UUID starts with a + number. E.g. UUID `"0210ae70-482b-4671-b758-35e13e0097a9"` would + convert to `210` through `.to_s`. Therefore, if someone else has + a "private" export with the prefixes "00000210", "0000210", + "000210", "00210", "0210" or "210", that would cause a collision + and the file could be attached to the wrong private export. + + Theoretical chance of collision (the reality depends on the + UUID generation algorithm): + + - Potential combinations of the UUID first part (8 characters hex): 16^8 + - Potentially colliding character combinations (8 numbers + characters in the range of 0-9): 10^8 + - 10^8 / 16^8 ≈ 2.3 (23 / 1000 users) + + The root cause is that the class `Decidim::PrivateExport` defines + an ActiveStorage relation to `file` and the table + `active_storage_attachments` stores the related `record_id` as + `bigint` which causes the conversion to happen. + + ### Workarounds + + Fully disable the private exports feature until a patch is available. +cvss_v4: 8.2 +unaffected_versions: + - "< 0.30.0" +patched_versions: + - "~> 0.30.4" + - ">= 0.31.0" +related: + url: + - https://nvd.nist.gov/vuln/detail/CVE-2025-65017 + - https://github.com/decidim/decidim/security/advisories/GHSA-3cx6-j9j4-54mp + - https://github.com/decidim/decidim/releases/tag/v0.31.0 + - https://github.com/decidim/decidim/releases/tag/v0.30.4 + - https://github.com/decidim/decidim/pull/13571 + - https://github.com/advisories/GHSA-3cx6-j9j4-54mp diff --git a/gems/decidim/CVE-2025-65017.yml b/gems/decidim/CVE-2025-65017.yml new file mode 100644 index 0000000000..74d056d9e8 --- /dev/null +++ b/gems/decidim/CVE-2025-65017.yml @@ -0,0 +1,132 @@ +--- +gem: decidim +cve: 2025-65017 +ghsa: 3cx6-j9j4-54mp +url: https://github.com/decidim/decidim/security/advisories/GHSA-3cx6-j9j4-54mp +title: Decidim's private data exports can lead to data leaks +date: 2026-02-03 +description: | + ### Impact + + Private data exports can lead to data leaks in cases where the UUID + generation causes collisions for the generated UUIDs. + + The bug was introduced by #13571 and affects Decidim versions 0.30.0 + or newer (currently 2025-09-23). + + This issue was discovered by running the following spec several + times in a row, as it can randomly fail due to this bug: + + ```bash + $ cd decidim-core + $ for i in {1..10}; do bundle exec rspec + spec/jobs/decidim/download_your_data_export_job_spec.rb + -e "deletes the" || break ; done + ``` + + Run the spec as many times as needed to hit a UUID that converts + to `0` through `.to_i`. + + The UUID to zero conversion does not cause a security issue but + the security issue is demonstrated with the following example. + + The following code regenerates the issue by assigning a predefined + UUID that will generate a collision (example assumes there are + already two existing users in the system): + + ```ruby + # Create the ZIP buffers to be stored + buffer1 = Zip::OutputStream.write_buffer do |out| + out.put_next_entry("admin.txt") + out.write "Hello, admin!" + end + buffer1.rewind + buffer2 = Zip::OutputStream.write_buffer do |out| + out.put_next_entry("user.txt") + out.write "Hello, user!" + end + buffer2.rewind + + # Create the private exports with a predefined IDs + user1 = Decidim::User.find(1) + export = user1.private_exports.build + export.id = "0210ae70-482b-4671-b758-35e13e0097a9" + export.export_type = "download_your_data" + export.file.attach(io: buffer1, filename: "foobar.zip", + content_type: "application/zip") + export.expires_at = Decidim.download_your_data_expiry_time.from_now + export.metadata = {} + export.save! + + user2 = Decidim::User.find(2) + export = user2.private_exports.build + export.id = "0210d2df-a0c7-40aa-ad97-2dae5083e3b8" + export.export_type = "download_your_data" + export.file.attach(io: buffer2, filename: "foobar.zip", + content_type: "application/zip") + export.expires_at = Decidim.download_your_data_expiry_time.from_now + export.metadata = {} + export.save! + ``` + + Expect to see an error in the situation. + + Now, login as user with ID 1, go to `/download_your_data`, click + "Download file" from the export and expect to see the data that + should be attached to user with ID 2. This is an artificially + replicated situation with the predefined UUIDs but it can easily + happen in real situations. + + The reason for the test case failure can be replicated in case + you change the export ID to + `export.id = "e9540f96-9e3d-4abe-8c2a-6c338d85a684"`. + This would return `0` through `.to_s` + + After attaching that ID, you can test if the file is available + for the export: + + ```ruby + user.private_exports.last.file.attached? + => false + user.private_exports.last.file.blob + => nil + ``` + + Note that this fails with such UUID as shown in the example and + could easily lead to collisions in case the UUID starts with a + number. E.g. UUID `"0210ae70-482b-4671-b758-35e13e0097a9"` would + convert to `210` through `.to_s`. Therefore, if someone else has + a "private" export with the prefixes "00000210", "0000210", + "000210", "00210", "0210" or "210", that would cause a collision + and the file could be attached to the wrong private export. + + Theoretical chance of collision (the reality depends on the + UUID generation algorithm): + + - Potential combinations of the UUID first part (8 characters hex): 16^8 + - Potentially colliding character combinations (8 numbers + characters in the range of 0-9): 10^8 + - 10^8 / 16^8 ≈ 2.3 (23 / 1000 users) + + The root cause is that the class `Decidim::PrivateExport` defines + an ActiveStorage relation to `file` and the table + `active_storage_attachments` stores the related `record_id` as + `bigint` which causes the conversion to happen. + + ### Workarounds + + Fully disable the private exports feature until a patch is available. +cvss_v4: 8.2 +unaffected_versions: + - "< 0.30.0" +patched_versions: + - "~> 0.30.4" + - ">= 0.31.0" +related: + url: + - https://nvd.nist.gov/vuln/detail/CVE-2025-65017 + - https://github.com/decidim/decidim/security/advisories/GHSA-3cx6-j9j4-54mp + - https://github.com/decidim/decidim/releases/tag/v0.31.0 + - https://github.com/decidim/decidim/releases/tag/v0.30.4 + - https://github.com/decidim/decidim/pull/13571 + - https://github.com/advisories/GHSA-3cx6-j9j4-54mp