Skip to content

Enforce ACL permission checks on state-changing admin routes - #467

Merged
navneetkumar-pim-webkul merged 2 commits into
unopim:masterfrom
dripar-webkul:fix/integrations-acl-privilege-escalation
Jun 4, 2026
Merged

navneetkumar-pim-webkul merged 2 commits into
unopim:masterfrom
dripar-webkul:fix/integrations-acl-privilege-escalation

Conversation

@dripar-webkul

Copy link
Copy Markdown
Collaborator

Summary

The Bouncer middleware only enforces a permission when the current route
name exists in the ACL map (config/acl.php → $acl->roles[]). Several
state-changing routes were missing from that map, so they were not covered
by any permission check — they were reachable by any authenticated admin
regardless of their assigned role.

This PR adds the missing route → permission mappings so these routes are
gated consistently with the rest of the codebase.

Root cause

hasPermission() is an exact route→key lookup; routes absent from
config/acl.php skip bouncer()->allow() entirely, and the affected
controllers have no inline bouncer() fallback.

Changes

Added the missing route → permission mappings in acl.php:

Route(s) Mapped permission
configuration.integrations.store / .update / .generate_key / .re_generate_secret_key configuration.integrations.create / .edit
catalog.products.bulk-edit.save / .save-media catalog.products.edit
catalog.families.completeness.edit / .update / .mass_update catalog.families.edit
configuration.store / .download configuration
catalog.products.bulkedit / .bulkedit.filters / bulkedit.attributes.fetch-all / products.check-variant / categories.tree catalog.products (view)

Mappings were chosen against the exact-match permission model and the
permission tree's ancestor auto-select behaviour, so legitimate users keep
their existing access
— read-only views and per-user actions (e.g.
notification.read_all) are intentionally left unchanged.

Tests

Extended WriteRouteBypassRegressionTest with cases asserting a restricted
(dashboard-only) user receives 403 on each newly mapped write route.

  • Pint: ✅ passed
  • Pest: ✅ 30/30 regression + 36/36 positive-path (Integration,
    IntegrationSection, ProductBulkEdit, CompletenessSettings)
  • Translations: ✅ 224/224 locales (existing keys reused, none added)
  • Playwright: unaffected — e2e runs as superadmin (permission_type=all),
    which bypasses Bouncer; no UI/string changes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants