Kinto Attachment's attachments can be replaced on read-only records
Description
Impact
The attachment file of an existing record can be replaced if the user has "read" permission on one of the parent (collection or bucket).
And if the "read" permission is given to "system.Everyone" on one of the parent, then the attachment can be replaced on a record using an anonymous request.
Note that if the parent has no explicit read permission, then the records attachments are safe.
Patches
- Patch released in kinto-attachment 6.4.0
- https://github.com/Kinto/kinto-attachment/commit/f4a31484f5925cbc02b59ebd37554538ab826ca1
Workarounds
None if the read permission has to remain granted.
Updating to 6.4.0 or applying the patch individually (if updating is not feasible) is strongly recommended.
References
- https://bugzilla.mozilla.org/show_bug.cgi?id=1879034
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
kinto-attachmentPyPI | < 6.4.0 | 6.4.0 |
Patches
1f4a31484f592Merge pull request from GHSA-hvp4-vrv2-8wrq
2 files changed · +50 −3
src/kinto_attachment/utils.py+12 −1 modified@@ -26,7 +26,11 @@ class AttachmentRouteFactory(RouteFactory): def __init__(self, request): - """Attachment is not a Kinto resource. + """ + This class is the `context` object being passed to the + :class:`kinto.core.authorization.AuthorizationPolicy`. + + Attachment is not a Kinto resource. The required permission is: * ``write`` if the related record exists; @@ -43,12 +47,19 @@ def __init__(self, request): existing = resource.get() except httpexceptions.HTTPNotFound: existing = None + if existing: + # Request write permission on the existing record. self.permission_object_id = record_uri(request) self.required_permission = "write" else: + # Request create record permission on the parent collection. self.permission_object_id = collection_uri(request) self.required_permission = "create" + # Set the current object in context, since it is used in the + # authorization policy to distinguish operations on plural endpoints + # from individual objects. See Kinto/kinto#918 + self.current_object = existing def sha256(content):
tests/test_views_attachment.py+38 −2 modified@@ -283,21 +283,57 @@ def test_upload_refused_if_not_authenticated(self): self.headers.pop("Authorization") self.upload(status=401) + def test_upload_replace_refused_if_not_authenticated(self): + self.upload(status=201) + + self.headers.pop("Authorization") + self.upload(status=401) + def test_upload_refused_if_not_allowed(self): self.headers.update(get_user_headers("jean-louis")) self.upload(status=403) def test_upload_replace_refused_if_only_create_allowed(self): - # Allow any authenticated to write in this bucket. + # Allow any authenticated to write in this collection. perm = {"permissions": {"record:create": ["system.Authenticated"]}} self.app.patch_json("/buckets/fennec/collections/fonts", perm, headers=self.headers) self.upload(status=201) self.headers.update(get_user_headers("jean-louis")) self.upload(status=403) + def test_upload_replace_refused_if_only_bucket_read_is_allowed(self): + # Create a record with attachment. + self.upload(status=201) + + # Now allow anyone to read this bucket. + perm = {"permissions": {"read": ["system.Everyone"]}} + self.app.patch_json("/buckets/fennec", perm, headers=self.headers) + + # And try to replace anonymously. + self.headers.pop("Authorization") + self.upload(status=401) + + def test_upload_replace_refused_if_only_read_is_allowed(self): + # Create a record with attachment. + self.upload(status=201) + + # Now allow anyone to read this collection. + perm_change = [ + {"op": "add", "path": "/permissions", "value": {"read": ["system.Everyone"]}} + ] + self.app.patch_json( + "/buckets/fennec/collections/fonts", + perm_change, + headers={**self.headers, "Content-Type": "application/json-patch+json"}, + ) + + # And try to replace anonymously. + self.headers.pop("Authorization") + self.upload(status=401) + def test_upload_create_accepted_if_create_allowed(self): - # Allow any authenticated to write in this bucket. + # Allow any authenticated to write in this collection. perm = {"permissions": {"record:create": ["system.Authenticated"]}} self.app.patch_json("/buckets/fennec/collections/fonts", perm, headers=self.headers)
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
4News mentions
0No linked articles in our index yet.