Skip to content

Harden installer state guards and seal install endpoints once installed - #459

Merged
navneetkumar-pim-webkul merged 7 commits into
unopim:masterfrom
dripar-webkul:fix/installer-pre-auth-admin-takeover
Jun 4, 2026
Merged

navneetkumar-pim-webkul merged 7 commits into
unopim:masterfrom
dripar-webkul:fix/installer-pre-auth-admin-takeover

Conversation

@dripar-webkul

Copy link
Copy Markdown
Collaborator

Description

Tightens the installer access controls so the /install routes and their API endpoints can no longer be reached once an instance is fully installed.

  • CanInstall middleware now seals all /install requests once installation is complete, based solely on the storage/installed completion marker (instead of the previous request-type-based check).
  • The completion marker is now written only at the genuine end of the install flow (after admin creation, and after demo data when opted in), so the installer cannot seal itself mid-flow.
  • Added a defence-in-depth guard (abortIfInstalled()) to every state-changing installer endpoint — env-file-setup, run-migration, run-seeder, admin-config-setup, seed-sample-data, smtp-config-setup — so they refuse to run on a live instance even if the middleware is bypassed.
  • Backend-only; no schema or user-facing string changes. Genuine install flow (fresh setup, demo-data opt-in, dev re-seed) is unaffected.

How To Test This?

Automated

  • ./vendor/bin/pest packages/Webkul/Installer/tests/Feature/InstallerSecurityTest.php → 9 passed
  • ./vendor/bin/pest --testsuite="Installer Feature Test" → 46 passed
  • Playwright: BASE_URL=http://127.0.0.1:<port> npx playwright test tests/08-security/installer-takeover.spec.js → 3 passed

Manual (on an installed instance)

  1. Send a POST to /install/api/admin-config-setup with header X-Requested-With: XMLHttpRequest and a JSON admin payload.
  2. Expect a 302 redirect to the dashboard (or 403) — not 200.
  3. Confirm admin id 1 in the admins table is unchanged and no new account was created.
  4. Verify a fresh install (no storage/installed marker) still completes normally end-to-end.

Documentation

  • My pull request requires an update on the documentation repository.

No documentation changes required.

Branch Selection

  • Target Branch: master

Pint

  • ./vendor/bin/pint — passed on all changed files.

Tailwind Reordering

  • N/A — no Blade/Tailwind/markup changes in this PR.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens the UnoPim installer by sealing /install routes and installer API endpoints after installation completes, using storage/installed as the completion marker and adding defense-in-depth guards inside installer endpoints.

Changes:

  • Seal all /install traffic once storage/installed exists (including XHR/AJAX), removing the previous AJAX bypass surface.
  • Add a controller-level abortIfInstalled() guard to state-changing installer endpoints, and ensure the completion marker is written at the end of the UI flow (admin step or demo-data step).
  • Add/adjust automated coverage (Pest + Playwright) around installer sealing behavior and marker semantics.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/e2e-pw/tests/08-security/installer-takeover.spec.js New Playwright e2e assertions that installer endpoints are sealed on installed instances.
packages/Webkul/Installer/tests/Feature/InstallerSecurityTest.php New feature tests covering AJAX-bypass regression, defense-in-depth controller forbids, and marker timing.
packages/Webkul/Installer/tests/Feature/InstallerMarkInstalledTest.php Verifies CLI installer sealing via markInstalled() and idempotent event dispatch.
packages/Webkul/Installer/tests/Feature/InstallerCsrfRetryTest.php Adjusts test harness to simulate “install in progress” by temporarily removing the installed marker.
packages/Webkul/Installer/tests/Feature/InstallerControllerTest.php Adjusts test harness to remove/restore the marker so controller tests don’t get blocked by the new guard.
packages/Webkul/Installer/src/Resources/views/installer/index.blade.php UI now posts seed_sample_data to allow correct “seal after demo-data” behavior.
packages/Webkul/Installer/src/Http/Middleware/CanInstall.php Installer sealing now applies to all /install requests once completed (no AJAX exception).
packages/Webkul/Installer/src/Http/Controllers/InstallerController.php Adds abortIfInstalled(), introduces UI-flow marker writing, removes dd() in favor of reporting + JSON error.
packages/Webkul/Installer/src/Console/Commands/Installer.php Ensures CLI installs always write storage/installed and dispatch unopim.installed exactly once.
Comments suppressed due to low confidence (1)

packages/Webkul/Installer/src/Http/Controllers/InstallerController.php:280

  • smtpConfigSetup() duplicates the marker write + event dispatch logic instead of reusing markInstalled(). Reusing the helper keeps the sealing behavior consistent (and benefits from markInstalled() idempotency).
        $this->environmentManager->setEnvConfiguration(request()->input());

        $filePath = storage_path('installed');

        File::put($filePath, 'Your UnoPim App is Successfully Installed');

        Event::dispatch('unopim.installed');

        return $filePath;

Comment thread packages/Webkul/Installer/src/Http/Middleware/CanInstall.php Outdated
Comment thread packages/Webkul/Installer/src/Http/Middleware/CanInstall.php Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

packages/Webkul/Installer/src/Http/Controllers/InstallerController.php:280

  • smtpConfigSetup() now has abortIfInstalled(), but it still writes storage/installed and dispatches unopim.installed directly. That bypasses the new markInstalled() idempotency guard and can re-dispatch the event / overwrite the marker contents. Reuse $this->markInstalled() here instead of duplicating the seal logic.
        $this->abortIfInstalled();

        $this->environmentManager->setEnvConfiguration(request()->input());

        $filePath = storage_path('installed');

        File::put($filePath, 'Your UnoPim App is Successfully Installed');

        Event::dispatch('unopim.installed');

Comment thread packages/Webkul/Installer/src/Console/Commands/Installer.php
@navneetkumar-pim-webkul
navneetkumar-pim-webkul merged commit 68f0b01 into unopim:master Jun 4, 2026
14 checks passed
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.

3 participants