New image attachment index breaks if image name contains a slash #11560

Closed
opened 2025-12-29 21:46:45 +01:00 by adam · 12 comments
Owner

Originally created by @fabi125 on GitHub (Sep 3, 2025).

Originally assigned to: @pheus on GitHub.

Deployment Type

Self-hosted

NetBox Version

v4.4.0

Python Version

3.12

Steps to Reproduce

  1. Go to a random site/rack/device, e.g. https://demo.netbox.dev/dcim/racks/1/
  2. Click on "Attach an image"
  3. Select a random image and then enter as the name something that contains a slash, e.g. todays date 9/3/25

Expected Behavior

Image gets added or an error is shown that says can't use a slash in the name.

Observed Behavior

<class 'IndexError'>

list index out of range

which comes from here:

  File "/opt/netbox/netbox/extras/models/models.py", line 731, in filename
    return os.path.basename(self.image.name).split('_', 2)[2]
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^
IndexError: list index out of range

The new index and code was added in #18990. This is currently preventing us from upgrading to v4.4 as we seem to have image attachments with slashes in their names in the database and on the filesystem (which are even nested into subfolders oO):

-[ RECORD 3 ]--+-------------------------------------------------------------
id             | 134
object_id      | 12
image          | image-attachments/rack_12_5/31/23.jpg
image_height   | 4032
image_width    | 3024
name           | 5/31/23
created        | 2023-06-01 15:43:29.221191+00
object_type_id | 29
last_updated   | 2023-06-01 15:43:29.221203+00
$ find media/image-attachments/rack_12_5/
media/image-attachments/rack_12_5/
media/image-attachments/rack_12_5/31
media/image-attachments/rack_12_5/31/23.jpg
Originally created by @fabi125 on GitHub (Sep 3, 2025). Originally assigned to: @pheus on GitHub. ### Deployment Type Self-hosted ### NetBox Version v4.4.0 ### Python Version 3.12 ### Steps to Reproduce 1. Go to a random site/rack/device, e.g. https://demo.netbox.dev/dcim/racks/1/ 2. Click on "Attach an image" 3. Select a random image and then enter as the name something that contains a slash, e.g. todays date `9/3/25` ### Expected Behavior Image gets added or an error is shown that says can't use a slash in the name. ### Observed Behavior ``` <class 'IndexError'> list index out of range ``` which comes from here: ``` File "/opt/netbox/netbox/extras/models/models.py", line 731, in filename return os.path.basename(self.image.name).split('_', 2)[2] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^ IndexError: list index out of range ``` The new index and code was added in #18990. This is currently preventing us from upgrading to v4.4 as we seem to have image attachments with slashes in their names in the database and on the filesystem (which are even nested into subfolders oO): ``` -[ RECORD 3 ]--+------------------------------------------------------------- id | 134 object_id | 12 image | image-attachments/rack_12_5/31/23.jpg image_height | 4032 image_width | 3024 name | 5/31/23 created | 2023-06-01 15:43:29.221191+00 object_type_id | 29 last_updated | 2023-06-01 15:43:29.221203+00 ``` ``` $ find media/image-attachments/rack_12_5/ media/image-attachments/rack_12_5/ media/image-attachments/rack_12_5/31 media/image-attachments/rack_12_5/31/23.jpg ```
adam added the type: bugstatus: acceptedseverity: low labels 2025-12-29 21:46:45 +01:00
adam closed this issue 2025-12-29 21:46:46 +01:00
Author
Owner

@bleuchtang commented on GitHub (Sep 3, 2025):

This issue breaks upgrade to 4.4.0 too:

./upgrade.sh
[...]
  ipam.vrf... Skipping (found 22 existing).                                                                                                                                                                                                                  
  extras.configcontextprofile... No objects found.                                                                            
  extras.customfield... Skipping (found 8 existing).                            
  extras.imageattachment... Traceback (most recent call last):                                                                
  File "/opt/netbox/venv/lib/python3.11/site-packages/django/db/models/options.py", line 683, in get_field                                                                                                                                                   
    return self.fields_map[field_name]                                                                                                                                                                                                                       
           ~~~~~~~~~~~~~~~^^^^^^^^^^^^
KeyError: 'filename' 

During handling of the above exception, another exception occurred:
                                                                                                                              
Traceback (most recent call last):                                                                                                                                                                                                                           
  File "/opt/netbox/netbox/netbox/search/__init__.py", line 108, in to_cache                                                                                                                                                                                 
    type_ = cls.get_field_type(instance, name)                                                                                                                                                                                                               
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                                                                                                                                                                                                               
  File "/opt/netbox/netbox/netbox/search/__init__.py", line 51, in get_field_type                                      
    field_cls = instance._meta.get_field(field_name).__class__                                                                                                                                                                                               
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                                                                                                                                                                                                         
  File "/opt/netbox/venv/lib/python3.11/site-packages/django/db/models/options.py", line 685, in get_field                                                                                                                                                   
    raise FieldDoesNotExist(                                                                                                  
django.core.exceptions.FieldDoesNotExist: ImageAttachment has no field named 'filename'                                                                                                                                                                      
                                                                                                                              
During handling of the above exception, another exception occurred:                                                                                                                                                                                          
                                                                                                                              
Traceback (most recent call last):
  File "/opt/netbox/netbox/manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/opt/netbox/venv/lib/python3.11/site-packages/django/core/management/__init__.py", line 442, in execute_from_command_line
    utility.execute()
  File "/opt/netbox/venv/lib/python3.11/site-packages/django/core/management/__init__.py", line 436, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)                                                                   
  File "/opt/netbox/venv/lib/python3.11/site-packages/django/core/management/base.py", line 416, in run_from_argv      
    self.execute(*args, **cmd_options)                                                                                        
  File "/opt/netbox/venv/lib/python3.11/site-packages/django/core/management/base.py", line 460, in execute           
    output = self.handle(*args, **options)                                                                                    
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                                                                                    
  File "/opt/netbox/netbox/extras/management/commands/reindex.py", line 96, in handle
    i = search_backend.cache(model.objects.iterator(), remove_existing=False)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox/netbox/netbox/search/backends.py", line 220, in cache
    for field in indexer.to_cache(instance, custom_fields=custom_fields):
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox/netbox/netbox/search/__init__.py", line 111, in to_cache
    type_ = cls.get_attr_type(instance, name)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^            
  File "/opt/netbox/netbox/netbox/search/__init__.py", line 67, in get_attr_type
    value = getattr(instance, field_name)                                                                                     
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox/netbox/extras/models/models.py", line 731, in filename
    return os.path.basename(self.image.name).split('_', 2)[2]                                                                 
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^                                                                 
IndexError: list index out of range

I was able to run the upgrade normally after changing the image path directly in postgresql and moving the image in media directory.

@bleuchtang commented on GitHub (Sep 3, 2025): This issue breaks upgrade to 4.4.0 too: ``` ./upgrade.sh [...] ipam.vrf... Skipping (found 22 existing). extras.configcontextprofile... No objects found. extras.customfield... Skipping (found 8 existing). extras.imageattachment... Traceback (most recent call last): File "/opt/netbox/venv/lib/python3.11/site-packages/django/db/models/options.py", line 683, in get_field return self.fields_map[field_name] ~~~~~~~~~~~~~~~^^^^^^^^^^^^ KeyError: 'filename' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/opt/netbox/netbox/netbox/search/__init__.py", line 108, in to_cache type_ = cls.get_field_type(instance, name) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/netbox/netbox/search/__init__.py", line 51, in get_field_type field_cls = instance._meta.get_field(field_name).__class__ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/venv/lib/python3.11/site-packages/django/db/models/options.py", line 685, in get_field raise FieldDoesNotExist( django.core.exceptions.FieldDoesNotExist: ImageAttachment has no field named 'filename' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/opt/netbox/netbox/manage.py", line 10, in <module> execute_from_command_line(sys.argv) File "/opt/netbox/venv/lib/python3.11/site-packages/django/core/management/__init__.py", line 442, in execute_from_command_line utility.execute() File "/opt/netbox/venv/lib/python3.11/site-packages/django/core/management/__init__.py", line 436, in execute self.fetch_command(subcommand).run_from_argv(self.argv) File "/opt/netbox/venv/lib/python3.11/site-packages/django/core/management/base.py", line 416, in run_from_argv self.execute(*args, **cmd_options) File "/opt/netbox/venv/lib/python3.11/site-packages/django/core/management/base.py", line 460, in execute output = self.handle(*args, **options) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/netbox/extras/management/commands/reindex.py", line 96, in handle i = search_backend.cache(model.objects.iterator(), remove_existing=False) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/netbox/netbox/search/backends.py", line 220, in cache for field in indexer.to_cache(instance, custom_fields=custom_fields): ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/netbox/netbox/search/__init__.py", line 111, in to_cache type_ = cls.get_attr_type(instance, name) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/netbox/netbox/search/__init__.py", line 67, in get_attr_type value = getattr(instance, field_name) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/netbox/netbox/extras/models/models.py", line 731, in filename return os.path.basename(self.image.name).split('_', 2)[2] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^ IndexError: list index out of range ``` I was able to run the upgrade normally after changing the image path directly in postgresql and moving the image in media directory.
Author
Owner

@DanSheps commented on GitHub (Sep 4, 2025):

How did you even manage to upload an image with slashes in the name?

@DanSheps commented on GitHub (Sep 4, 2025): How did you even manage to upload an image with slashes in the name?
Author
Owner

@fabi125 commented on GitHub (Sep 4, 2025):

How did you even manage to upload an image with slashes in the name?

As mentioned in my initial bug report, you can specify a name when you upload an image and that gets used as the filename (or directory name in this case, lol):

Steps to Reproduce

  1. Go to a random site/rack/device, e.g. https://demo.netbox.dev/dcim/racks/1/

  2. Click on "Attach an image"

  3. Select a random image and then enter as the name something that contains a slash, e.g. todays date 9/3/25

@fabi125 commented on GitHub (Sep 4, 2025): > How did you even manage to upload an image with slashes in the name? As mentioned in my initial bug report, you can specify a name when you upload an image and that gets used as the filename (or directory name in this case, lol): > ### Steps to Reproduce > > 1. Go to a random site/rack/device, e.g. https://demo.netbox.dev/dcim/racks/1/ > > 2. Click on "Attach an image" > > 3. Select a random image and then enter as the name something that contains a slash, e.g. todays date `9/3/25`
Author
Owner

@DanSheps commented on GitHub (Sep 4, 2025):

Might need to sanitize that a little better then, lol

@DanSheps commented on GitHub (Sep 4, 2025): Might need to sanitize that a little better then, lol
Author
Owner

@bctiemann commented on GitHub (Sep 4, 2025):

It "shouldn't" happen — even on a Mac which allows slashes in filenames, NetBox handles the upload fine (by stripping the slashes out); but then on MacOS slashes are stored as : at the Unix layer, and translated to slashes in the UI for tradition's sake.

If slashes do get through one way or another, we should at least be able to handle it gracefully.

@bctiemann commented on GitHub (Sep 4, 2025): It "shouldn't" happen — even on a Mac which allows slashes in filenames, NetBox handles the upload fine (by stripping the slashes out); but then on MacOS slashes are stored as `:` at the Unix layer, and translated to slashes in the UI for tradition's sake. If slashes do get through one way or another, we should at least be able to handle it gracefully.
Author
Owner

@fabi125 commented on GitHub (Sep 4, 2025):

To be extra clear here: the problem here is not the filename of the file that gets uploaded (because slashes either can't exist there or get removed), but rather the name field when you add the field where you can put in whatever your like (incl slashes) which then gets used as the filename for the uploaded image. Surprisingly this even creates needed nested directories if the name contains slashes.

I personally agree that this should be better sanitized and just not allow slashes or the system should use a uuid (or something) for the filename on the disk.

@fabi125 commented on GitHub (Sep 4, 2025): To be extra clear here: the problem here is not the filename of the file that gets uploaded (because slashes either can't exist there or get removed), but rather the name field when you add the field where you can put in whatever your like (incl slashes) which then gets used as the filename for the uploaded image. Surprisingly this even creates needed nested directories if the name contains slashes. I personally agree that this should be better sanitized and just not allow slashes or the system should use a uuid (or something) for the filename on the disk.
Author
Owner

@ljarasius commented on GitHub (Sep 8, 2025):

I personally agree that this should be better sanitized and just not allow slashes or the system should use a uuid (or something) for the filename on the disk.

I agree with @fabi125 here, these are both quite sane options. Alternatively, there is already an ID in the database for the image attachment that could also be used instead of the name.

@ljarasius commented on GitHub (Sep 8, 2025): > I personally agree that this should be better sanitized and just not allow slashes or the system should use a uuid (or something) for the filename on the disk. I agree with @fabi125 here, these are both quite sane options. Alternatively, there is already an ID in the database for the image attachment that could also be used instead of the name.
Author
Owner

@pheus commented on GitHub (Sep 10, 2025):

I’d like to take this. Could you assign me? Thanks!

I have a tentative fix (sanitize name, replace slashes, normalize in upload_to, and guard the filename parsing), but I’m a bit unsure whether you’d prefer rejecting slashes vs. silently normalizing. Happy to follow your guidance.

@pheus commented on GitHub (Sep 10, 2025): I’d like to take this. Could you assign me? Thanks! I have a tentative fix (sanitize `name`, replace slashes, normalize in `upload_to`, and guard the filename parsing), but I’m a bit unsure whether you’d prefer rejecting slashes vs. silently normalizing. Happy to follow your guidance.
Author
Owner

@bctiemann commented on GitHub (Sep 10, 2025):

Thanks @pheus . I think silently normalizing is preferable, since there's no expectation that filenames should be preserved through the upload process or match what's on the desktop machine.

@bctiemann commented on GitHub (Sep 10, 2025): Thanks @pheus . I think silently normalizing is preferable, since there's no expectation that filenames should be preserved through the upload process or match what's on the desktop machine.
Author
Owner

@fabi125 commented on GitHub (Sep 10, 2025):

Thanks @pheus . I think silently normalizing is preferable, since there's no expectation that filenames should be preserved through the upload process or match what's on the desktop machine.

To reiterate what I already said multiple times before in this issue: The problem is NOT the filename of the image that the user uploads, but the name that the user can enter after uploading the image:

Image

So I don't think it's a good idea to just silently change what the user entered there.

@fabi125 commented on GitHub (Sep 10, 2025): > Thanks [@pheus](https://github.com/pheus) . I think silently normalizing is preferable, since there's no expectation that filenames should be preserved through the upload process or match what's on the desktop machine. To reiterate what I already said multiple times before in this issue: The problem is NOT the filename of the image that the user uploads, but the name that the user can enter after uploading the image: <img width="949" height="484" alt="Image" src="https://github.com/user-attachments/assets/6ca323b2-3b55-4a6c-a840-50c9028dbba8" /> So I don't think it's a good idea to just silently change what the user entered there.
Author
Owner

@pheus commented on GitHub (Sep 10, 2025):

@fabi125 Thanks for the clarification!

You’re right that the concern is the user‑entered name (after upload), not the original file’s name. In the approach I’m proposing, the name stored and shown in the UI remains exactly as entered. Only the on‑disk filename is normalized (e.g., replacing path separators) to avoid creating subdirectories. In other words, we preserve what the user typed while keeping storage safe.

Does this align with your expectation?

Form:

Image

DetailView:

Image
@pheus commented on GitHub (Sep 10, 2025): @fabi125 Thanks for the clarification! You’re right that the concern is the **user‑entered** `name` (after upload), not the original file’s name. In the approach I’m proposing, the `name` stored and shown in the UI remains exactly as entered. Only the **on‑disk filename** is normalized (e.g., replacing path separators) to avoid creating subdirectories. In other words, we preserve what the user typed while keeping storage safe. Does this align with your expectation? Form: <img width="1474" height="608" alt="Image" src="https://github.com/user-attachments/assets/b5743106-f675-41ad-989f-cc5c4d3ebfba" /> DetailView: <img width="939" height="424" alt="Image" src="https://github.com/user-attachments/assets/aefa3f26-fae6-4a0c-abea-bb0a5fe4db7b" />
Author
Owner

@fabi125 commented on GitHub (Sep 10, 2025):

@pheus that sounds perfect, thanks!

@fabi125 commented on GitHub (Sep 10, 2025): @pheus that sounds perfect, thanks!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/netbox#11560