Opened 2 months ago
Last modified 17 hours ago
#37081 assigned Bug
loaddata fails when a directory in the fixture path contains a dot
| Reported by: | Alisson Silveira | Owned by: | Alisson Silveira |
|---|---|---|---|
| Component: | Core (Management commands) | Version: | dev |
| Severity: | Normal | Keywords: | |
| Cc: | Alisson Silveira | Triage Stage: | Accepted |
| Has patch: | yes | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
How to reproduce:
Create a fixture in a directory containing a dot, for example:
fixtures/fix.v1/data.json
Run (without specifying the file extension):
python manage.py loaddata fixtures/fix.v1/data
This raises:
CommandError: Problem installing fixture 'fixtures/fix': v1/data is not a known serialization format.
This occurs when a directory in the fixture path contains a dot, as the full path is incorrectly split on "." instead of operating on the file name.
Expected behavior: Django should correctly determine the fixture format based on the file name and load the fixture successfully, ignoring dots in directory names.
A fix has been identified and will be submitted in a pull request.
Change History (7)
comment:1 by , 2 months ago
| Owner: | set to |
|---|---|
| Status: | new → assigned |
comment:2 by , 2 months ago
| Has patch: | set |
|---|
comment:3 by , 8 weeks ago
Tested this on stable/6.0.x and 5.2.11, as reported, if the fixture extension is not specified then any other dot in the path will incorrectly be considered the separator for the extension.
follow-ups: 6 7 comment:4 by , 7 weeks ago
| Triage Stage: | Unreviewed → Accepted |
|---|
Thank you! I think this is a bug worth addressing.
My only concern was whether this could create any path traversal issues allowing something like fixtures/../secret.json but I think this would be out of the scope of security issues anyway. See https://docs.djangoproject.com/en/6.0/internals/security/#how-does-django-evaluate-a-report
comment:5 by , 7 weeks ago
| Version: | 6.0 → dev |
|---|
comment:6 by , 7 weeks ago
Replying to Sarah Boyce:
Thank you! I think this is a bug worth addressing.
My only concern was whether this could create any path traversal issues allowing something likefixtures/../secret.jsonbut I think this would be out of the scope of security issues anyway. See https://docs.djangoproject.com/en/6.0/internals/security/#how-does-django-evaluate-a-report
Thanks for your feedback, Sarah! I can provide a bit more context for the record.
I decided to use a PurePath object to prevent any filesystem access, since this method only needs to parse the filename. The filename is the only piece of information being extracted and modified from the filepath. In contrast, the current approach relies on rsplit() directly on the file path string, which could potentially introduce security issues if the path is manipulated. Using PurePath makes the intent clearer and provides safer path handling.
Since this was a bug I encountered in a production system, my fix is specifically focused on addressing that issue. However, I’d be more than happy to address any additional fixes or improvements related to this area if needed. Please let me know if you’d like me to explore any further improvements here.
comment:7 by , 17 hours ago
Replying to Sarah Boyce:
Thank you! I think this is a bug worth addressing.
My only concern was whether this could create any path traversal issues allowing something likefixtures/../secret.jsonbut I think this would be out of the scope of security issues anyway. See https://docs.djangoproject.com/en/6.0/internals/security/#how-does-django-evaluate-a-report
Thank you Sarah for addressing this. I was following your suggestion and I wrote this test:
from django.core.management.commands.loaddata import Command from django.core import serializers from django.test import SimpleTestCase class ParseNameTests(SimpleTestCase): def setUp(self): self.command = Command() self.command.serialization_formats = serializers.get_public_serializer_formats() def test_parent_traversal_no_longer_raises(self): name, ser_fmt, cmp_fmt = self.command.parse_name("../secret") self.assertEqual(name, "../secret") self.assertIsNone(ser_fmt) self.assertIsNone(cmp_fmt)
This test passes with the PurePath fix. Without it, parse_name("../secret") used to raise CommandError but only by accident: the dots in ".." get treated as format delimiters, triggering the same "unknown serialization format" error this ticket is about but for a different reason.
Note that a fixture name with an explicit extension, e.g. "../secret.json", is already parsed correctly even without this fix.
Even though this isn't a blocker, I think it's worth highlighting.
Pull request: https://github.com/django/django/pull/21213