#23197·netbox

REST /sync/ action ignores ObjectPermission constraints on the sync permission

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. Grant a user two object permissions on ExportTemplate: add + view with no constraints, and sync constrained to {"name": "Allowed"}.
  2. Create two export templates, "Allowed" and "Forbidden", each with a data file attached.
  3. POST to /api/extras/export-templates//sync/.
  4. For contrast, POST to the UI equivalent, /extras/export-templates//sync/, as the same user.

Expected Behavior

The constraint on the sync permission decides which objects the user can sync, over the API just as in the UI. Syncing "Forbidden" should be refused.

Observed Behavior

The API syncs it. The constraint is never evaluated, so a sync permission scoped to one object effectively grants sync over every object of that type the user can otherwise reach.

Suspected Cause

  • The permission check is request.user.has_perm(permission) with no object. ObjectPermissionMixin.has_perm() (netbox/netbox/authentication/init.py:136-139) returns True as soon as obj is None — the presence of the permission means the user has it for some object, and constraints are only evaluated when an object is supplied. So a constrained sync permission passes unconditionally.
  • The object is then fetched from self.queryset, which BaseViewSet.initial() has restricted for the action mapped to the HTTP method (netbox/netbox/api/viewsets/init.py:100-101). This is a POST, so that's add. The user's add constraints end up gating which objects can be synced.

The two enforcing paths do it correctly: DataSourceViewSet.sync passes obj=datasource into has_perm (netbox/core/api/views.py:49), and ObjectSyncDataView restricts the queryset on 'sync' (netbox/netbox/views/generic/feature_views.py:226). The generic mixin does neither.

Affects the four viewsets that mix in SyncedDataMixin: ExportTemplateViewSet, ConfigContextViewSet, ConfigTemplateViewSet, ConfigContextProfileViewSet (netbox/extras/api/views.py:120,225,231,241).

Proposed Fix

Mirror what the other two paths already do: resolve the object from self.queryset.restrict(request.user, 'sync'), and pass the object into the permission check.