Skip to content

Commit 90e2384

Browse files
jasnowpostmodern
authored andcommitted
GHSA SYNC: 2 brand new decideim-related advisories
1 parent f65bb8e commit 90e2384

File tree

2 files changed

+264
-0
lines changed

2 files changed

+264
-0
lines changed
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
---
2+
gem: decidim-core
3+
cve: 2025-65017
4+
ghsa: 3cx6-j9j4-54mp
5+
url: https://github.com/decidim/decidim/security/advisories/GHSA-3cx6-j9j4-54mp
6+
title: Decidim's private data exports can lead to data leaks
7+
date: 2026-02-03
8+
description: |
9+
### Impact
10+
11+
Private data exports can lead to data leaks in cases where the UUID
12+
generation causes collisions for the generated UUIDs.
13+
14+
The bug was introduced by #13571 and affects Decidim versions 0.30.0
15+
or newer (currently 2025-09-23).
16+
17+
This issue was discovered by running the following spec several
18+
times in a row, as it can randomly fail due to this bug:
19+
20+
```bash
21+
$ cd decidim-core
22+
$ for i in {1..10}; do bundle exec rspec
23+
spec/jobs/decidim/download_your_data_export_job_spec.rb
24+
-e "deletes the" || break ; done
25+
```
26+
27+
Run the spec as many times as needed to hit a UUID that converts
28+
to `0` through `.to_i`.
29+
30+
The UUID to zero conversion does not cause a security issue but
31+
the security issue is demonstrated with the following example.
32+
33+
The following code regenerates the issue by assigning a predefined
34+
UUID that will generate a collision (example assumes there are
35+
already two existing users in the system):
36+
37+
```ruby
38+
# Create the ZIP buffers to be stored
39+
buffer1 = Zip::OutputStream.write_buffer do |out|
40+
out.put_next_entry("admin.txt")
41+
out.write "Hello, admin!"
42+
end
43+
buffer1.rewind
44+
buffer2 = Zip::OutputStream.write_buffer do |out|
45+
out.put_next_entry("user.txt")
46+
out.write "Hello, user!"
47+
end
48+
buffer2.rewind
49+
50+
# Create the private exports with a predefined IDs
51+
user1 = Decidim::User.find(1)
52+
export = user1.private_exports.build
53+
export.id = "0210ae70-482b-4671-b758-35e13e0097a9"
54+
export.export_type = "download_your_data"
55+
export.file.attach(io: buffer1, filename: "foobar.zip",
56+
content_type: "application/zip")
57+
export.expires_at = Decidim.download_your_data_expiry_time.from_now
58+
export.metadata = {}
59+
export.save!
60+
61+
user2 = Decidim::User.find(2)
62+
export = user2.private_exports.build
63+
export.id = "0210d2df-a0c7-40aa-ad97-2dae5083e3b8"
64+
export.export_type = "download_your_data"
65+
export.file.attach(io: buffer2, filename: "foobar.zip",
66+
content_type: "application/zip")
67+
export.expires_at = Decidim.download_your_data_expiry_time.from_now
68+
export.metadata = {}
69+
export.save!
70+
```
71+
72+
Expect to see an error in the situation.
73+
74+
Now, login as user with ID 1, go to `/download_your_data`, click
75+
"Download file" from the export and expect to see the data that
76+
should be attached to user with ID 2. This is an artificially
77+
replicated situation with the predefined UUIDs but it can easily
78+
happen in real situations.
79+
80+
The reason for the test case failure can be replicated in case
81+
you change the export ID to
82+
`export.id = "e9540f96-9e3d-4abe-8c2a-6c338d85a684"`.
83+
This would return `0` through `.to_s`
84+
85+
After attaching that ID, you can test if the file is available
86+
for the export:
87+
88+
```ruby
89+
user.private_exports.last.file.attached?
90+
=> false
91+
user.private_exports.last.file.blob
92+
=> nil
93+
```
94+
95+
Note that this fails with such UUID as shown in the example and
96+
could easily lead to collisions in case the UUID starts with a
97+
number. E.g. UUID `"0210ae70-482b-4671-b758-35e13e0097a9"` would
98+
convert to `210` through `.to_s`. Therefore, if someone else has
99+
a "private" export with the prefixes "00000210", "0000210",
100+
"000210", "00210", "0210" or "210", that would cause a collision
101+
and the file could be attached to the wrong private export.
102+
103+
Theoretical chance of collision (the reality depends on the
104+
UUID generation algorithm):
105+
106+
- Potential combinations of the UUID first part (8 characters hex): 16^8
107+
- Potentially colliding character combinations (8 numbers
108+
characters in the range of 0-9): 10^8
109+
- 10^8 / 16^8 ≈ 2.3 (23 / 1000 users)
110+
111+
The root cause is that the class `Decidim::PrivateExport` defines
112+
an ActiveStorage relation to `file` and the table
113+
`active_storage_attachments` stores the related `record_id` as
114+
`bigint` which causes the conversion to happen.
115+
116+
### Workarounds
117+
118+
Fully disable the private exports feature until a patch is available.
119+
cvss_v4: 8.2
120+
unaffected_versions:
121+
- "< 0.30.0"
122+
patched_versions:
123+
- "~> 0.30.4"
124+
- ">= 0.31.0"
125+
related:
126+
url:
127+
- https://nvd.nist.gov/vuln/detail/CVE-2025-65017
128+
- https://github.com/decidim/decidim/security/advisories/GHSA-3cx6-j9j4-54mp
129+
- https://github.com/decidim/decidim/releases/tag/v0.31.0
130+
- https://github.com/decidim/decidim/releases/tag/v0.30.4
131+
- https://github.com/decidim/decidim/pull/13571
132+
- https://github.com/advisories/GHSA-3cx6-j9j4-54mp

gems/decidim/CVE-2025-65017.yml

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
---
2+
gem: decidim
3+
cve: 2025-65017
4+
ghsa: 3cx6-j9j4-54mp
5+
url: https://github.com/decidim/decidim/security/advisories/GHSA-3cx6-j9j4-54mp
6+
title: Decidim's private data exports can lead to data leaks
7+
date: 2026-02-03
8+
description: |
9+
### Impact
10+
11+
Private data exports can lead to data leaks in cases where the UUID
12+
generation causes collisions for the generated UUIDs.
13+
14+
The bug was introduced by #13571 and affects Decidim versions 0.30.0
15+
or newer (currently 2025-09-23).
16+
17+
This issue was discovered by running the following spec several
18+
times in a row, as it can randomly fail due to this bug:
19+
20+
```bash
21+
$ cd decidim-core
22+
$ for i in {1..10}; do bundle exec rspec
23+
spec/jobs/decidim/download_your_data_export_job_spec.rb
24+
-e "deletes the" || break ; done
25+
```
26+
27+
Run the spec as many times as needed to hit a UUID that converts
28+
to `0` through `.to_i`.
29+
30+
The UUID to zero conversion does not cause a security issue but
31+
the security issue is demonstrated with the following example.
32+
33+
The following code regenerates the issue by assigning a predefined
34+
UUID that will generate a collision (example assumes there are
35+
already two existing users in the system):
36+
37+
```ruby
38+
# Create the ZIP buffers to be stored
39+
buffer1 = Zip::OutputStream.write_buffer do |out|
40+
out.put_next_entry("admin.txt")
41+
out.write "Hello, admin!"
42+
end
43+
buffer1.rewind
44+
buffer2 = Zip::OutputStream.write_buffer do |out|
45+
out.put_next_entry("user.txt")
46+
out.write "Hello, user!"
47+
end
48+
buffer2.rewind
49+
50+
# Create the private exports with a predefined IDs
51+
user1 = Decidim::User.find(1)
52+
export = user1.private_exports.build
53+
export.id = "0210ae70-482b-4671-b758-35e13e0097a9"
54+
export.export_type = "download_your_data"
55+
export.file.attach(io: buffer1, filename: "foobar.zip",
56+
content_type: "application/zip")
57+
export.expires_at = Decidim.download_your_data_expiry_time.from_now
58+
export.metadata = {}
59+
export.save!
60+
61+
user2 = Decidim::User.find(2)
62+
export = user2.private_exports.build
63+
export.id = "0210d2df-a0c7-40aa-ad97-2dae5083e3b8"
64+
export.export_type = "download_your_data"
65+
export.file.attach(io: buffer2, filename: "foobar.zip",
66+
content_type: "application/zip")
67+
export.expires_at = Decidim.download_your_data_expiry_time.from_now
68+
export.metadata = {}
69+
export.save!
70+
```
71+
72+
Expect to see an error in the situation.
73+
74+
Now, login as user with ID 1, go to `/download_your_data`, click
75+
"Download file" from the export and expect to see the data that
76+
should be attached to user with ID 2. This is an artificially
77+
replicated situation with the predefined UUIDs but it can easily
78+
happen in real situations.
79+
80+
The reason for the test case failure can be replicated in case
81+
you change the export ID to
82+
`export.id = "e9540f96-9e3d-4abe-8c2a-6c338d85a684"`.
83+
This would return `0` through `.to_s`
84+
85+
After attaching that ID, you can test if the file is available
86+
for the export:
87+
88+
```ruby
89+
user.private_exports.last.file.attached?
90+
=> false
91+
user.private_exports.last.file.blob
92+
=> nil
93+
```
94+
95+
Note that this fails with such UUID as shown in the example and
96+
could easily lead to collisions in case the UUID starts with a
97+
number. E.g. UUID `"0210ae70-482b-4671-b758-35e13e0097a9"` would
98+
convert to `210` through `.to_s`. Therefore, if someone else has
99+
a "private" export with the prefixes "00000210", "0000210",
100+
"000210", "00210", "0210" or "210", that would cause a collision
101+
and the file could be attached to the wrong private export.
102+
103+
Theoretical chance of collision (the reality depends on the
104+
UUID generation algorithm):
105+
106+
- Potential combinations of the UUID first part (8 characters hex): 16^8
107+
- Potentially colliding character combinations (8 numbers
108+
characters in the range of 0-9): 10^8
109+
- 10^8 / 16^8 ≈ 2.3 (23 / 1000 users)
110+
111+
The root cause is that the class `Decidim::PrivateExport` defines
112+
an ActiveStorage relation to `file` and the table
113+
`active_storage_attachments` stores the related `record_id` as
114+
`bigint` which causes the conversion to happen.
115+
116+
### Workarounds
117+
118+
Fully disable the private exports feature until a patch is available.
119+
cvss_v4: 8.2
120+
unaffected_versions:
121+
- "< 0.30.0"
122+
patched_versions:
123+
- "~> 0.30.4"
124+
- ">= 0.31.0"
125+
related:
126+
url:
127+
- https://nvd.nist.gov/vuln/detail/CVE-2025-65017
128+
- https://github.com/decidim/decidim/security/advisories/GHSA-3cx6-j9j4-54mp
129+
- https://github.com/decidim/decidim/releases/tag/v0.31.0
130+
- https://github.com/decidim/decidim/releases/tag/v0.30.4
131+
- https://github.com/decidim/decidim/pull/13571
132+
- https://github.com/advisories/GHSA-3cx6-j9j4-54mp

0 commit comments

Comments
 (0)