#23198·netbox

Rack elevation ?highlight= allows arbitrary ORM field paths, leaking unviewable data

Author: arthansonCreated Sep 17, 2026Updated Sep 17, 2026
Labelstype: bugstatus: needs ownerseverity: mediumnetbox

NetBox Edition

NetBox Community

NetBox Version

v4.7.1

Python Version

3.12

Steps to Reproduce

  1. Give a user view on Rack and Device and nothing else — confirm GET /api/tenancy/tenants/ returns 403.
  2. Put two devices in a rack; assign one a tenant whose description is PROJECT-BLACKBIRD-2027.
  3. Request GET /api/dcim/racks//elevation/?render=svg&highlight=tenant__description__startswith:P.
  4. Look for class="slot shaded" in the returned SVG. It appears when the predicate matched at least one device, and is absent when it didn't.
  5. Walk the alphabet one character at a time.

Expected Behavior

highlight selects which of the devices already visible to the caller get emphasized. It shouldn't let the caller author arbitrary ORM predicates, and it shouldn't answer questions about objects they have no permission to view.

Observed Behavior

The caller supplies both the field path and the lookup operator. The shading in the rendered SVG is a clean one-bit answer to any predicate they can express, which makes the endpoint a general-purpose read oracle over everything reachable from Device by ORM traversal.

Suspected Cause

netbox/dcim/api/views.py:216-220 splits each highlight value on the first colon and passes the halves through untouched. netbox/dcim/svg/racks.py:119-125 then does q |= Q(**{k: v}).

permitted_devices is restricted with restrict(user, 'view'), which constrains which rows can match but not what the predicate is allowed to ask about. A predicate that traverses a relation is evaluated in the database against objects the restriction never covers, and the boolean result comes back through the shading either way.

Proposed Fix

allowlist the field and pin the operator to exact match, validating the key before constructing the Q(). Given that both in-tree callers only ever emit highlight=id: (netbox/dcim/views.py:1163, netbox/dcim/views.py:3024), the allowlist can be very small. Catch ValueError alongside FieldError there as well.