Repository navigation
[Security] Fix ORDER BY SQL injection via sort order in Product DataGrid - #488
Merged
navneetkumar-pim-webkul merged 6 commits intoJun 10, 2026
Conversation
3 tasks done
navneetkumar-pim-webkul
added a commit
that referenced
this pull request
Jun 26, 2026
…ixes) (#525) * Fix: enforce ACL on remaining write-verb admin routes Audit of the Bouncer middleware route-name ACL check revealed the same bypass pattern as the roles fix (commit 960b12b) across many admin sections: only the GET form routes (.create / .edit) were declared in acl.php, while the actual POST .store and PUT .update sibling routes were unmapped, so low-privileged admins could submit forms they could not legitimately view. Map the missing write-verb routes to the existing permission keys following the established duplicate-key pattern. Sections covered: - catalog.products.update - catalog.categories.store / update - catalog.category_fields.store / update - catalog.attributes.store / update - catalog.attributes.options.store / update / delete / update_sort - catalog.attribute.groups.store / update - catalog.families.store / update - settings.channels.store / update - settings.currencies.store / update - settings.locales.update - settings.data_transfer.imports.store / update / cancel / pause / resume / upload_images_zip - settings.data_transfer.exports.store / update Attribute option sub-routes are gated by catalog.attributes.edit (managing options is part of attribute edit). Import lifecycle actions (cancel/pause/resume) are gated by data_transfer.imports.execute. Add Pest regression tests covering each newly mapped route. Out of scope (separate ticket): configuration.integrations.*, configuration.store, history.version.delete/restore, magic_ai.platform.* / prompt.* / system_prompt.*, catalog.products.bulk-edit.*, catalog.products.check-variant, catalog.families.completeness.update. These need new permission keys or product-owner input. * chore(release): v2.1.1 — ACL write-route bypass patch Adds CHANGELOG entry for the security fix landed in 5f84257. * Fix: harden against Host / X-Forwarded-Host header poisoning Laravel's url(), asset(), and Vite helpers resolve against the request Host header by default. Combined with trustProxies(at: '*'), this lets any client cause UnoPim to render asset URLs (admin <meta base-url>, Vite <script src>, TinyMCE document_base_url) pointing at an attacker origin, yielding full frontend takeover. Layered fix: 1. Pin URL::forceRootUrl + URL::forceScheme to config('app.url') in CoreServiceProvider::boot so every url()/asset()/Vite call resolves against APP_URL regardless of any Host / X-Forwarded-Host value. 2. Replace url()->to('/') and asset('/') in the admin layout, history layout, anonymous layout, and TinyMCE component with rtrim(config('app.url'), '/') so the rendered output never reads from the request even if a future change drops layer 1. 3. Restrict trustProxies from '*' to env('TRUSTED_PROXIES','127.0.0.1') so X-Forwarded-* headers from arbitrary clients are ignored. 4. Enable trustHosts(at: ...) seeded from APP_URL + TRUSTED_HOSTS so Symfony returns 400 in non-local environments when the Host header is unrecognised. Documents TRUSTED_PROXIES and TRUSTED_HOSTS in .env.example. Adds a Pest regression suite that spoofs Host and X-Forwarded-Host headers and asserts the rendered base-url meta + asset()/url() output stays pinned to APP_URL. * fix: cast booleans + bypass FK + filter sequences in demo extras seeder for pgsql (#418) * fix: render styled 405 Method Not Allowed page and add translations for all locales (#417) * fix: reject special characters in DB_DATABASE to prevent reinstall failure (#419) * fix(installer): trim DB/Elasticsearch inputs and apply transform: trim(...) across installer prompts (#420) * Update code for chat-latest model temperature not supported (#416) * fix(installer): preserve user locales/currencies during demo seed (#401) * fix(installer): preserve user-selected locales/currencies during demo seeding * refactor: optimize installer demo seed locale/currency handling * fix(installer): redirect to install.php when vendor/autoload.php is missing (#400) * Fix: restored the ui dropdown, and removed the hyphen from placeholder text (#399) * fix(admin): hide product edit More button when no actions are available (#397) * Fix: fixed the hide/unhide more button on the product edit page * Fix: added translation for more text and fixed the test script * Fix: fixed the dynamic column on the product page (#396) * fix(webhook): validate URL on save + rename menu to "Webhook" (#398) * fix(webhook): validate URL with test probe before save and rename menu to singular * fix(webhook): probe URL before save, rename menu to singular, fix flaky e2e * fix(webhook): make logs status filter driver-agnostic and per-code * Add Gravatar fallback for admin avatars (#357) * Add gravatar-based admin avatar fallback with local proxy * Fix Blade conflict for Vue image error handler * fix: address gravatar PR review feedback * feat(admin): ACL-aware login redirect, datagrid copy/share-edit actions, conditional row actions - SessionController: redirect post-login to first menu item the admin actually has ACL on (route-level key via app('acl')->roles), with fallback scan of acl config and logout when nothing is accessible - AdminServiceProvider: expose adminLandingUrl (first authorized menu URL) to views; header logo now uses it instead of hardcoded dashboard route - Datagrid: support new 'copy' and 'edit-share' action methods, plus per-row action 'condition' callback on Action/DataGrid to hide actions per record - Datagrid: listen for share-link-changed emitter event to refetch; cleanup on beforeUnmount - Header: revert avatar to admin->image / image_url path - Lang: add link-copied / copy-failed strings, prefix select placeholders with "--", drop unused 'more' key * fix(i18n): add link-copied / copy-failed and drop 'more' orphan in 32 locales Propagates the en_US additions (admin.components.datagrid.index.link-copied and copy-failed) and the removal of the unused datagrid 'more' label to all 33 supported locales, restoring Translation Integrity Audit pass. * test(user): allow null intended URL in open-redirect tests SessionController no longer stores url.intended when the referer is from a foreign host (it skips the put instead of defaulting to the dashboard). A null intended URL is equally safe — login then falls back to the ACL-aware landing URL — so the test should only enforce that nothing attacker-controlled is persisted, not that an internal URL is. * test(e2e): update Hindi-translate test to match current modal markup The translate modal (translate-action.blade.php) renders step indicators as numbered circles with "Select Source" / "Select Target" labels — the literal "Step 1" text was removed in #372 but this assertion was never updated, causing the test to time out on every push-event run on master. Switch the visibility check to the "Select Source" label which is the actual user-visible text when the modal opens. * Update chat-latest model for temperature not supported * fix(admin): strip HTML from datagrid cell title tooltip (#403) * Add Playwright REST API test suite and CI improvements (#413) * test(api): add Playwright REST API test suite (211 tests) * fix(test): make API tests run in CI without pre-seeded creds - oauth.spec.js: lazy-refresh creds after apiToken bootstrap fixture (previously credsReady was frozen at file load → always skipped) - auth-helper.js: fall back ADMIN_EMAIL to ADMIN_USERNAME (the env var name used by the existing playwright_test.yml workflow) * fix(test): make API tests run in CI without pre-seeded creds * Increase shard count for Playwright tests to 8 * Update Playwright test shards to 10 total * Reduce Playwright test shards from 10 to 8 * Clean up comments in OAuth test file Removed comments explaining performance considerations for OAuth tests. * Address Copilot review comments on API test suite - schema-validator: drop unsupported keywords from fallback comment; cache compiled Ajv validators via WeakMap to avoid per-call recompile - config.js / .env.example: correct .api-config.json path to tests/e2e-pw/ (matches actual resolution) - utils/api-config.js: add 'use strict' for consistency with siblings - tests/api/README.md: align parallelism docs with the real playwright.config (fullyParallel: false, dynamic worker count) - playwright.config.js: drop hardcoded "4 shards" from inline comment * Address Copilot review: clarify validation-error helper and reap category-fields - expectValidationError: update docstring to match the restricted [400, 422, 500] allowlist instead of claiming "any 4xx/5xx", so the comment no longer overstates what the helper accepts. - media-upload spec: track the category-field created in 15.6 and delete it in afterAll so repeated runs don't leak cf_media_* rows. --------- Co-authored-by: kunal kumar <[email protected]> * Update AiApiClient.php * Update regex to include 'chat-latest' model * version2.1 * Update AgentRunner.php * Update AiApiClient.php * Update regex to include 'chat-latest' model * Updated * Updated chat-latest model for not support temperature * Updated * Update AgentRunner.php * Update AiApiClient.php * Update LaravelAiAdapter.php --------- Co-authored-by: Dripar gupta <[email protected]> Co-authored-by: Prince Kumar Sahni <[email protected]> Co-authored-by: Johannes Rudolph <[email protected]> Co-authored-by: Navneet Kumar <[email protected]> Co-authored-by: Kunal kumar <[email protected]> Co-authored-by: kunal kumar <[email protected]> * docs(security): formalise rolling support policy; mark 1.0 EOL Update SECURITY.md to reflect the 4-branch reality: - 2.1.x active (current stable) - 2.0.x security-only until ~90 days after 2.2 ships - 1.0.x End of Life — v1.0.1 is the final release - 0.x lines remain EOL Adds rationale for 1.0 EOL (Laravel 10 EOL Aug 2026, pre-v11 bootstrap, per-branch backport cost), clarifies the patched-release flow, and points reporters at GitHub Security Advisories alongside the email channel. * docs(security): adopt Laravel-style support matrix (sync from master) * Fix/docker multi arch publish (#426) * ci(docker): resolve version with semver validation and :latest gating * ci(docker): add QEMU setup and per-image tag computation * ci(docker): build and push multi-arch (amd64+arm64) web image * ci(docker): build and push multi-arch (amd64+arm64) queue worker image * ci(docker): write multi-arch publish summary to GITHUB_STEP_SUMMARY * ci(docker): gate :minor floating tag, fix MINOR for pre-releases, harden tag input Review feedback addressed: - MINOR was derived from VERSION via ${VERSION%.*}, which on tags like v2.1.0-beta.2 produced '2.1.0-beta' (last .suffix stripped). Now strip the -suffix first via ${VERSION%%-*} so MINOR is always the bare X.Y. - :minor floating tag was always pushed, so a pre-release like v2.1.0-rc1 would overwrite the stable :2.1 floating tag — the exact class of bug this PR is fixing for :latest. Gate :minor behind is_prerelease == false to match :latest gating. - Move release/input tag from inline \${{ }} into env to avoid shell injection via a tag name containing shell metacharacters. Required contents:write to exploit, but cheap to harden. Manual trace verified for v2.1.0, v2.1.0-rc1, v2.1.0-beta.2, v2.1.0-alpha.1-build.5, v10.20.30. * ci(docker): smoke-test multi-arch manifests + add queue HEALTHCHECK Two best-practice additions on top of the multi-arch publish: 1. Smoke-test step after both builds: - docker buildx imagetools inspect verifies manifest list shape contains both linux/amd64 and linux/arm64 (regex match on raw manifest JSON, no false positives). - docker run --platform linux/amd64|arm64 boots the image and runs 'php -v' as a minimal liveness check. Catches QEMU-segfault class regressions before users hit them. - set -euo pipefail + ::error:: annotations + ::group:: log folding. - Tag value passed via env (VERSION) not inline templating, so shell metacharacters in a tag cannot inject. 2. HEALTHCHECK in q.Dockerfile: - pgrep -f matches either 'artisan queue:work' or 'artisan schedule:work' (the same image runs both via overridden entrypoint for the scheduler service). - Bracket-trick pattern '[a]rtisan ...' keeps pgrep from matching its own command line (verified locally). - 60s start period gives Laravel boot + initial DB ping time to complete before the first probe. * fix(docker): smoke-test entrypoint override, procps for HEALTHCHECK, workflow concurrency Fixes the two bugs the previous commit introduced and hardens the workflow with industry-standard concurrency + timeout controls. 1. Smoke test now overrides ENTRYPOINT: web-entrypoint.sh and q-entrypoint.sh require a live database and either run migrations or wait for the install lock file. Passing 'php -v' as CMD reaches the entrypoint, not the PHP binary, so the container always exited non-zero before the smoke check could run. `--entrypoint php` bypasses the runtime entrypoint and exercises only the PHP runtime — sufficient to catch QEMU-segfault / glibc regressions, which is the bug class this smoke test exists for. 2. Wrap each docker run in `timeout 90s`: prevents a QEMU stall from hanging the whole job until the workflow timeout fires. 3. Install procps in q.Dockerfile: the HEALTHCHECK uses pgrep, which lives in procps. The php:8.3-cli base (debian-slim) does not include procps, so the previous commit would have made every queue container report unhealthy on first probe. 4. Workflow-level concurrency + per-job timeout: - concurrency.group keyed on the release / dispatch tag prevents two simultaneous publishes from racing on the same registry tag. - timeout-minutes: 90 caps the job below the default 360-minute ceiling so a stuck arm64 build fails predictably. * fix(docker): widen queue HEALTHCHECK start_period to cover install lock wait q-entrypoint.sh waits up to 300s (SETUP_WAIT_TIMEOUT) for the application server to finish first-time setup and write storage/unopim.lock before launching `php artisan queue:work`. With start_period=60s the container would be marked unhealthy after the first ~150s while still legitimately waiting for setup. Widen start_period to 360s (300s wait + 60s margin) so the orchestrator only flags genuine post-startup failures. Also document the push-then-smoke trade-off in the workflow: multi-arch builds cannot use load:true (local docker holds only one arch), so the smoke test runs after push. A failure means the bad image is already public and requires manual rollback. * fix(ci): allow hyphens in semver pre-release identifiers (#426 review) Copilot review on PR #426 flagged the previous regex rejected valid SemVer pre-release identifiers containing hyphens. Per SemVer 2.0.0 §9, identifiers comprise ASCII alphanumerics + hyphens (separated by dots), so tags like v2.1.0-rc-1 and v2.1.0-alpha-build.5 are legitimate release shapes. Old pattern: '(-[A-Za-z0-9.]+)?' New pattern: '(-[0-9A-Za-z.-]+)?' Trace verified: PASS: v2.1.0, v2.1.0-rc1, v2.1.0-beta.2, v2.1.0-rc-1, v2.1.0-alpha-build.5, v2.1.0-x-y-z, v10.20.30 REJECT: vfoo, v2.1, v2.1.0- (trailing hyphen with no suffix) * fix(ci/docker): address Copilot PR #427 review feedback Four valid findings, all fixed: 1. HEALTHCHECK ignores operator-overridable SETUP_WAIT_TIMEOUT: The previous 360s start_period assumed the default 300s wait. If an operator raises SETUP_WAIT_TIMEOUT, the container would be marked unhealthy while still legitimately waiting for the install lock, triggering restart loops in some orchestrators. Rewrite the check so the lock file's presence is part of the condition: no lock yet → report healthy (worker still waiting for web/fpm setup); lock present → require an active queue:work or schedule:work process. start_period drops back to 60s since the probe now self-adjusts. 2. workflow_dispatch can build the wrong code: For release events github.sha already equals the tag commit, so the initial checkout is correct. For workflow_dispatch the initial checkout is the dispatched-from ref (master/2.1/whatever), which can differ from the `tag` input the operator typed. A `v2.0.5` dispatch from master would otherwise publish master's code tagged as 2.0.5. Add a `Re-checkout tag (workflow_dispatch)` step that runs only on dispatch events. It fetches the tag and checks it out in detached HEAD before any of the tag-compute or build steps see the workspace. 3. + 4. :minor gating comment misleading (web + queue tag steps): The code intentionally gates :minor on `is_prerelease=false` only, not on `on_default_branch`, so a stable hotfix on a side branch (v2.0.5 on the 2.0 line) updates :2.0 without touching :latest. The previous comment said "default-branch line", which implied a stricter gate than the code actually enforces. Rewrite both comments to explain the intent: :minor tracks the latest stable on its own release line; only :latest is tied to the default branch. * docs(security): split Supported vs End of Life sections * docs(changelog): expand v2.1.1 release notes Date bumped to 2026-05-26 (actual release day). Add the four bullet groups that were folded into the v2.1.1 line after the initial security tag — installer fixes (#419, #420), styled 405 page (#417), pgsql demo seeder (#418), MagicAI chat-latest temperature (#416), and the multi-arch Docker publish (#426). Keeps the two original security bullets at the top. * ci: retrigger workflows after GitHub setup-php fetch glitch * ci: pin shivammathur/setup-php to v2.37.1 GitHub Actions runner is caching a stale v2 tag resolution (SHA 7c071dfe...) that no longer exists on github.com/shivammathur/setup-php, so every job using setup-php@v2 fails at 'Set up job' with: An action could not be found at the URI 'https://codeload.github.com/shivammathur/setup-php/tar.gz/7c071dfe...' Pinning to the latest released tag v2.37.1 (current actual HEAD of v2) bypasses the stale cache and unblocks Linting / Pest / Playwright / Translation / Pest-PgSQL workflows until GitHub refreshes the action resolver. * ci: correct setup-php tag (drop v prefix) shivammathur/setup-php releases as '2.37.1' not 'v2.37.1'. Fix the pin so the action resolver can find it. * Update CHANGELOG.md * docs(security): drop GitHub Security Advisories channel (sync from master) Private vulnerability reporting is disabled on the repository so the GHSA 'Report a vulnerability' button does not exist. Keep email ([email protected]) as the single channel. * fix(webhook): validate URL on save, rename menu to Webhook, and harden logs status filter (#448) * chore(webhook): harden webhook URL validation and dispatch [v2.1 backport] (#450) * chore(release): v2.1.2 — harden webhook URL handling (#455) * [2.1] Harden installer: seal /install routes and endpoints once installed (#460) * fix: seal installer to prevent pre-auth admin takeover via AJAX bypass * fix(installer): seal installer on Docker setup to close pre-auth admin takeover * [Backport 2.1] fix(installer): remove hardcoded admin default credentials & make seeders idempotent (#462) * fix(installer): remove hardcoded admin default credentials and make seeders idempotent (backport #457) * ci(e2e): pass installer admin creds to seeder and read them in login util so Playwright login matches the seeded admin * Enforce ACL permission checks on state-changing admin routes (2.1 backport) (#468) * chore(release): v2.1.2 — harden webhook URL handling * Enforce ACL permission checks on state-changing admin routes * feat: add debug-only APP_URL mismatch guard (AppUrlGuard) (#465) * chore(release): v2.1.3 — installer & ACL security hardening, AppUrlGuard (#473) * fix(ci): remove empty ${{ }} expression breaking Docker Publish workflow (#471) An empty "${{ }}" inside the Resolve-Version run-block comment is parsed by GitHub Actions as an expression interpolation. Empty braces fail validation ("An expression was expected", L50), invalidating the whole workflow at startup. As a result every release: published event on the 2.1 line silently failed to dispatch, so no Docker image was built or pushed (e.g. v2.1.2 never reached Docker Hub) and the demo never updated. Reword the comment to plain text, matching the already-fixed master. Backports the master fix to the 2.1 default branch so v2.1.3+ releases auto-publish. * docs(agents): update AI agent instructions from Laravel 11 → Laravel 12 / PHP 8.3 (#466) The project's composer.json requires laravel/framework ^12.0 and php ^8.3, but six agent-instruction files (read by Copilot, Kilo Code, Claude, Cursor, and Codex at session start) still describe the project as 'Laravel 11' and '.github/copilot-instructions.md' still requires 'PHP 8.1+'. This causes AI agents to apply Laravel 11 idioms and target the wrong PHP version when generating code. Files updated: - AGENTS.md: Laravel 11 → Laravel 12 - code-generation-instructions.md (root + .github/): Laravel 11 → Laravel 12 - code-review-instructions.md (root + .github/): Laravel 11 → Laravel 12 - .github/copilot-instructions.md: * Laravel 11 → Laravel 12, PHP 8.1+ → PHP 8.3+ * Section 1 was actively misleading: it told agents to hardcode the 'wk_' table prefix, which contradicts AGENTS.md ('Never hardcode wk_ in table names — it causes wk_wk_ double prefix issues') and the actual production code (Channel.php → 'channels', CoreConfig.php → 'core_config'). The section has been rewritten to point at the real unprefixed-name convention, with corrected examples. Co-authored-by: dashitongzhi <[email protected]> * Clarify DB table prefix guidance to avoid Laravel double-prefixing (#470) * Initial plan * Clarify DB prefix behavior in Copilot instructions --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: Navneet Kumar - Webkul <[email protected]> * Feat/help and installer 2.1 (#474) * docs: add design spec for Help & Resources menu section (cherry picked from commit 3c6bc06) * docs: add implementation plan for Help & Resources menu section (cherry picked from commit 0847184) * feat(admin): add reusable global card component (cherry picked from commit 3e065bd) * refactor(admin): harden card rel attr + document trusted icon output (cherry picked from commit 470fe10) * feat(admin): add help config with sections and cta (cherry picked from commit 5f71a7d) * feat(admin): add en_US translations for help section (cherry picked from commit 4166d0a) * feat(admin): add help route and controller (cherry picked from commit 3bec3ad) * feat(admin): add help page view using card component (cherry picked from commit a0b710c) * feat(admin): add help sidebar menu and acl entries (cherry picked from commit b474fb8) * i18n(admin): translate help section into all supported locales (cherry picked from commit cd908fe) * docs(installer): spec for optional packages + cloud hosting banner (cherry picked from commit 43d0c8b462d96ba272a4542ac47f59438658c3c3) * feat(installer): optional add-on packages + cloud hosting banner Add a multiselect (and --with-packages CSV option) to unopim:install so operators can pull in official add-ons (DAM, Shopify, Bagisto connectors). Each is installed after core via composer require + the package's own artisan installer in a fresh process, with optimize:clear and queue:restart around it, pinned to the resolved DB connection. Always renders a UnoPim cloud-hosting promo banner at the end. * feat(admin): finalize Help & Resources page and card component Source refinements from 99529f2 (card.blade.php + help/index.blade.php), without the master-only cleanup deletions / rebuilt assets. * feat(admin): add dismissible cloud-hosting & version-upgrade promo banners Carousel promo bar injected at the top of every admin page via the content.before view event. Cloud-hosting slide always shows; the upgrade slide appears only when the running version is behind the latest release (remote Packagist check, cached, fail-silent). Each slide has its own per-user 'Don't show again' persisted in DB; the upgrade dismissal is version-scoped so a newer release re-shows it. Reusable <x-admin::promo-bar> anonymous component + inline v-promo-bar Vue (auto-rotate, dots, axios dismiss). Strings translated across all 33 locales. (cherry picked from commit 064617fd9927e614e769d3ba643fb7dd7e4cdfc8) * fix(admin): correct help service URLs, pin Help menu last, offset layout for promo bar - Point Services cards at live pages (support-maintenance-services, pim-connector-development) and API docs at devdocs.unopim.com/2.1/api. - Document the Help menu sentinel sort (99999) that keeps it pinned last. - Offset fixed header/sidebar via :has(#unopim-promo-bar) so the promo bar no longer cuts the left menu; make the bar sticky at top. * build(admin): recompile theme assets with promo bar + help page styles Rebuild app.css from 2.1 source so the promo-bar/help Tailwind arbitrary classes (z-[10050], text-[13.5px], max-[820px], h-[30px], etc.) are present. JS unchanged in spirit — promo bar is registered inline in its blade. * cleanup extra files * fix(installer): ask optional-package selection before demo seeding Demo-data seeding runs Artisan::call() internally, which flips the console input to non-interactive. Resolving the package multiselect afterwards silently skipped it when the user chose to seed sample products. Resolve the selection up front while still interactive; install the packages at the end. * feat(installer): non-interactive flags --modules and --sampledata Add --modules (alias for --with-packages) and --sampledata=yes|no (alias for --with-demo-data) so a fully scripted install needs no prompts. The interactive 'sample products?' question now yields to either demo-data flag, so passing one never blocks or double-seeds. * revert(installer): drop duplicate --modules/--sampledata aliases Redundant with the existing --with-packages and --with-demo-data flags. Keep the real fixes: resolve the package selection up front, and skip the interactive 'sample products?' prompt when --with-demo-data is set or the run is non-interactive. * fix(installer): gate prompts on a real TTY, not isInteractive() GitHub Actions reports input->isInteractive() == true with no STDIN attached, so the up-front package multiselect prompted and aborted on EOF, failing the install step (and thus every Pest/Playwright job). Gate the multiselect and the 'sample products?' prompt on stream_isatty(STDIN) via hasInteractiveTerminal(); headless/CI installs skip them, real terminals still prompt. * feat(installer): revamp web installer + CLI add-ons, docs, translations Web installer: admin-themed UI (cloud top bar, vertical stepper, card panels, language switcher), config-first/install-last flow, live SSE terminal that streams database → migrate → seed → admin → optional sample data → optional add-on packages (DAM/Shopify/Bagisto, installed server-side where shell exists). Adds Elasticsearch setup, DB auto-create (MySQL + pgsql), runtime .env reload, and shared/FTP-only hosting safeguards. UI polish: requirement/DB grids, bounded allowed-locale/currency lists, outline Back buttons, browser-origin URL, UTC tz. Fixes web-context command registration for demo seeding and persists add-on/ sample selections to a transient state file (session was unreliable across the install). Pins the Help menu/ACL entry last. Adds the new installer keys to all 33 locales, a v2.1.4 changelog entry, and a Cloud Hosting section in the README. * fix(admin): render flash messages above the promo banner The promo banner (z-[10050]) overlapped the flash-message stack (z-[10003]), covering the 'Close' button and breaking catalog E2E specs that dismiss flashes. Raise the flash group to z-[10060] so notifications sit above the banner. * fix(admin): keep promo banner in flow so it never overlays UI * fix(admin): address PR review — guard version JSON, atomic dismissal, bind promo timer - VersionCheck: bail to fallback when Packagist JSON does not decode to an array (avoids array-offset-on-null warnings). - AdminPromoDismissalRepository: use firstOrCreate so the unique-index dismissal write stays idempotent under concurrent requests. - promo-bar: bind the rotation interval via an arrow fn so `this` is preserved. * fix(installer): address PR review — server-side promo version, safe .env writes, driver-based db create - HelpController: derive dismissal version server-side instead of trusting client - EnvironmentManager: preg_replace_callback so $ in passwords is not a backreference - DatabaseManager: branch on db driver, not connection name, for pgsql create * refactor(admin): use Laravel HTTP client instead of curl in VersionCheck * chore(core): bump version to 2.1.4 * Chore(deps): Bump symfony/polyfill-intl-idn from 1.33.0 to 1.38.1 (#481) Bumps [symfony/polyfill-intl-idn](https://github.com/symfony/polyfill-intl-idn) from 1.33.0 to 1.38.1. - [Release notes](https://github.com/symfony/polyfill-intl-idn/releases) - [Commits](symfony/polyfill-intl-idn@v1.33.0...v1.38.1) --- updated-dependencies: - dependency-name: symfony/polyfill-intl-idn dependency-version: 1.38.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Chore(deps): Bump symfony/http-kernel from 7.4.7 to 7.4.13 (#482) Bumps [symfony/http-kernel](https://github.com/symfony/http-kernel) from 7.4.7 to 7.4.13. - [Release notes](https://github.com/symfony/http-kernel/releases) - [Changelog](https://github.com/symfony/http-kernel/blob/8.2/CHANGELOG.md) - [Commits](symfony/http-kernel@v7.4.7...v7.4.13) --- updated-dependencies: - dependency-name: symfony/http-kernel dependency-version: 7.4.13 dependency-type: indirect ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Navneet Kumar - Webkul <[email protected]> * Chore(deps): Bump phpoffice/phpspreadsheet from 1.30.4 to 1.30.5 (#487) Bumps [phpoffice/phpspreadsheet](https://github.com/PHPOffice/PhpSpreadsheet) from 1.30.4 to 1.30.5. - [Release notes](https://github.com/PHPOffice/PhpSpreadsheet/releases) - [Changelog](https://github.com/PHPOffice/PhpSpreadsheet/blob/1.30.5/CHANGELOG.md) - [Commits](PHPOffice/PhpSpreadsheet@1.30.4...1.30.5) --- updated-dependencies: - dependency-name: phpoffice/phpspreadsheet dependency-version: 1.30.5 dependency-type: indirect ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Chore(deps): Bump symfony/mailer from 7.4.6 to 7.4.12 (#485) Bumps [symfony/mailer](https://github.com/symfony/mailer) from 7.4.6 to 7.4.12. - [Release notes](https://github.com/symfony/mailer/releases) - [Changelog](https://github.com/symfony/mailer/blob/8.2/CHANGELOG.md) - [Commits](symfony/mailer@v7.4.6...v7.4.12) --- updated-dependencies: - dependency-name: symfony/mailer dependency-version: 7.4.12 dependency-type: indirect ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix: make promo banner sticky to remove empty gap on scroll (#480) * fix: make promo banner sticky to remove empty gap on scroll Promo banner used position:relative, so it scrolled out of view while the layout already offsets the header by 3rem (its height). When the banner scrolled away the sticky header stayed pinned at top:48px, leaving a 48px empty gap above it. Make the banner sticky (top-0, z above header) so it stays pinned and the header sits flush below it. * fix: drop promo bar z-index to avoid modal overlay collision Addresses review feedback: z-[10002] tied modal overlays (confirm/history/ bulk-edit all use z-[10002]) and, being rendered later in the DOM, painted above them — leaving the banner visible/clickable over an open modal. The banner only needs to stay above scrolling page content (which it does via position:sticky alone, as a positioned element) and below modal overlays. Removing the explicit z-index achieves both. Also avoids relying on a new arbitrary Tailwind value (z-[10000]) that the committed build CSS does not contain (CI does not rebuild assets). * fix: give promo bar z-[9999] to sit above transform-stacked charts Dashboard chart SVGs use CSS transform, so each creates its own stacking context. With the banner at z-index:auto, those contexts painted at the same level and (being later in the DOM) bled over the sticky banner on scroll — e.g. completeness donut charts appeared inside the banner. A positive z-index is required to beat transform stacking contexts. z-[9999] sits above all page content (<=1000) while staying below the header/modal/drawer overlays (10001/10002), so modals and drawers still cover the banner correctly. z-[9999] already exists in the committed build CSS (CI does not rebuild assets), so no asset rebuild is needed. * fix(admin): validate TinyMCE uploads to image allowlist and store with randomised name (#476) Co-authored-by: Navneet Kumar - Webkul <[email protected]> * [Security] Fix ORDER BY SQL injection via sort order in Product DataGrid (#488) * fix(datagrid): allowlist product sort direction to prevent ORDER BY SQL injection * refactor: optimize code --------- Co-authored-by: Navneet Kumar - Webkul <[email protected]> * Chore(deps): Bump symfony/routing from 7.4.6 to 7.4.13 (#483) Bumps [symfony/routing](https://github.com/symfony/routing) from 7.4.6 to 7.4.13. - [Release notes](https://github.com/symfony/routing/releases) - [Changelog](https://github.com/symfony/routing/blob/8.2/CHANGELOG.md) - [Commits](symfony/routing@v7.4.6...v7.4.13) --- updated-dependencies: - dependency-name: symfony/routing dependency-version: 7.4.13 dependency-type: indirect ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Navneet Kumar - Webkul <[email protected]> * Chore(deps-dev): Bump symfony/yaml from 7.4.6 to 7.4.13 (#486) Bumps [symfony/yaml](https://github.com/symfony/yaml) from 7.4.6 to 7.4.13. - [Release notes](https://github.com/symfony/yaml/releases) - [Changelog](https://github.com/symfony/yaml/blob/8.2/CHANGELOG.md) - [Commits](symfony/yaml@v7.4.6...v7.4.13) --- updated-dependencies: - dependency-name: symfony/yaml dependency-version: 7.4.13 dependency-type: indirect ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Navneet Kumar - Webkul <[email protected]> * fix(api): close fail-open scope bypass on configurable-product write routes (#477) Co-authored-by: Navneet Kumar - Webkul <[email protected]> * Update Cloud Hosting section in README Removed and re-added the Cloud Hosting section with an image link for better visibility. * Fix MagicAI Platform authorization bypass: update & set-default reachable without permission (#479) * fix(acl): require platform-edit permission for MagicAI platform update and set-default * packages/Webkul/refactor: optimize code * Fix stored XSS via channel name breaking out of HTML attribute (family completeness) (#489) * fix(xss): escape channel options on family completeness page * test(e2e): log in over HTTP in global-setup so storageState is authenticated * refactor: optimize code * Chore(deps): Bump guzzlehttp/psr7 from 2.9.0 to 2.11.0 (#496) Bumps [guzzlehttp/psr7](https://github.com/guzzle/psr7) from 2.9.0 to 2.11.0. - [Release notes](https://github.com/guzzle/psr7/releases) - [Changelog](https://github.com/guzzle/psr7/blob/2.11/CHANGELOG.md) - [Commits](guzzle/psr7@2.9.0...2.11.0) --- updated-dependencies: - dependency-name: guzzlehttp/psr7 dependency-version: 2.11.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix authorization bypass on MagicAI prompt/system-prompt and AiAgent generate routes [2.1] (#494) * [Security] Fix authorization bypass on MagicAI prompt/system-prompt and AiAgent generate routes * refactor(acl): enforce ai-agent.generate.process via acl map instead of inline middleware --------- Co-authored-by: Navneet Kumar - Webkul <[email protected]> * fix:Login lockout shows 500 Internal Server Error page instead of 429 Too Many Requests when APP_DEBUG=false (#499) Co-authored-by: Navneet Kumar - Webkul <[email protected]> * Validate and sanitize attribute swatch image uploads [2.1] (#495) * fix(upload): sanitize & validate attribute swatch image uploads via FileStorer * refactor: optimize code --------- Co-authored-by: Navneet Kumar - Webkul <[email protected]> * feat(installer): add type-to-search locale & currency prompts to CLI installer (#491) Co-authored-by: Navneet Kumar - Webkul <[email protected]> Co-authored-by: Navneet Kumar - Webkul <[email protected]> * chore: v2.1.5 patch cleanup — upgrade-script fixes, skills extraction, dead-file removal (#493) * chore(github): overhaul issue templates, PR template, contributing guide - single bug-report form with required environment checklist and duplicate-search confirmation; remove legacy markdown duplicate - fix label case and add title prefixes so labels auto-apply - issue chooser: add Discussions and documentation links - CONTRIBUTING: fix dead template links, document conventional commits, branch targets, security policy and local CI checks - PR template: fix malformed HTML comments, actionable checklist * chore: move AI agent skills to unopim/agent-skills repository Skills now live in https://github.com/unopim/agent-skills and install via 'npx skills add unopim/agent-skills'. Remove in-repo skill copies, agent symlink dirs, the skills-consistency workflow and validator, and the connector instruction files (moved to the skills repo). Ignore agent directories so local installs never get committed. * fix(installer): upgrade script and web-installer composer handling upgrade.sh: - backup now includes storage/ user data (media); previously a restore lost all uploads. Excludes only deps, caches, logs, debugbar - copy step includes dotfiles (.env.example, .gitignore were dropped) - anchored .env parsing; passwords with '=' or quotes no longer break the dump; credentials passed via MYSQL_PWD/PGPASSWORD env vars - PostgreSQL support via pg_dump based on DB_CONNECTION - rm -f the temp dump so a failed dump cannot abort the run (set -e) - drop dead UPGRADE_TO_VERSION stub install.php: COMPOSER_HOME now points to a writable directory (storage/composer) instead of the phar file path. InstallerController: probe bundled bin/composer/composer.phar in resolveComposerBinary(); paths extracted to composerProbePaths() with tests. Stabilise installer tests with DatabaseTransactions so runs no longer pollute the dev database. Docs: consolidate UPGRADE.md (remove stale per-release impact lists and dead link), drop superseded per-version upgrade guides, fix README upgrade link, add pg_dump backup instructions. * chore: remove dead files, update bundled composer, add favicon - remove unused leftovers: public/forge, patches.lock.json (plugin not installed), config/horizon.php and config/sitemap.php (packages not installed), broken bin/codecept symlink, placeholder DataGrid test and its now-empty phpunit suite entries - update bundled composer.phar 2.8.3 -> 2.10.1 - add public/favicon.ico (browsers request it unconditionally) - changelog: draft v2.1.5 entry --------- Co-authored-by: Dripar gupta <[email protected]> * docs(changelog): add v2.1.x security fixes (#492) Co-authored-by: Navneet Kumar - Webkul <[email protected]> * Update CHANGELOG.md (#506) * chore: bump version to 2.1.5 - Core::VERSION 2.1.4 -> 2.1.5 - Remove unused version field from private package.json * Fixed #502 - Accept uppercase image extensions (.JPG/.JPEG) (#511) * Fix: Index audits, skip empty translation audits, and fix history preview (#509) Co-authored-by: Navneet Kumar - Webkul <[email protected]> * Fixed Confirm before demo-data seeding overwrites existing data[2.1] (#503) * fixed: Confirm before demo-data seeding overwrites existing data * Fixed:Confirm and guard demo-data seeding to prevent data loss * refactor: shorten code comments --------- Co-authored-by: Navneet Kumar - Webkul <[email protected]> * Chore(deps): Bump laravel/framework from 12.55.1 to 12.61.1 (#517) Bumps [laravel/framework](https://github.com/laravel/framework) from 12.55.1 to 12.61.1. - [Release notes](https://github.com/laravel/framework/releases) - [Changelog](https://github.com/laravel/framework/blob/13.x/CHANGELOG.md) - [Commits](laravel/framework@v12.55.1...v12.61.1) --- updated-dependencies: - dependency-name: laravel/framework dependency-version: 12.61.1 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Chore(deps): Bump phpseclib/phpseclib from 3.0.52 to 3.0.55 (#516) Bumps [phpseclib/phpseclib](https://github.com/phpseclib/phpseclib) from 3.0.52 to 3.0.55. - [Release notes](https://github.com/phpseclib/phpseclib/releases) - [Changelog](https://github.com/phpseclib/phpseclib/blob/master/CHANGELOG.md) - [Commits](phpseclib/phpseclib@3.0.52...3.0.55) --- updated-dependencies: - dependency-name: phpseclib/phpseclib dependency-version: 3.0.55 dependency-type: indirect ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Navneet Kumar - Webkul <[email protected]> * Preselect application locale and currency from .env during reinstallation (#514) * Feat: added the default selection value on installer cli with search * Chore: class name rename --------- Co-authored-by: Navneet Kumar - Webkul <[email protected]> * fix(i18n): add 3 missing translation keys across all 32 locales Resolve pre-existing translation parity gaps where en_US carried keys the other locales lacked (surfaced during the 2.1 convergence; present on master independently of it): - Admin: settings.data-transfer.exports.create.search-categories - Installer: seeders.core.currencies.CAD - Installer: installer.index.environment-configuration.dollar Added with natural per-language translations (not English copies) to all 32 non-English locales. en_US untouched; en_AU/en_GB/en_NZ use English text. unopim:translations:check: 258/258 locales pass. Pint clean. --------- Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: Dripar gupta <[email protected]> Co-authored-by: Pravat Senapati <[email protected]> Co-authored-by: Prince Kumar Sahni <[email protected]> Co-authored-by: Johannes Rudolph <[email protected]> Co-authored-by: Kunal kumar <[email protected]> Co-authored-by: kunal kumar <[email protected]> Co-authored-by: Kral <[email protected]> Co-authored-by: dashitongzhi <[email protected]> Co-authored-by: Copilot <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Issue Reference
Fixes High finding SQL injection (ORDER BY) in the Product DataGrid.
Description
ProductDataGrid::processRequestedSorting()concatenated the request-suppliedsort direction (
sort[order]) directly into a raw SQL string viaorderByRaw(),and the base DataGrid only validated that
sortis an array — there was noallowlist restricting the direction to
asc/desc. As a result an attacker couldinject arbitrary SQL into the ORDER BY clause and perform blind/error-based data
exfiltration (e.g. from the admins table). The raw branch runs whenever
sort[column]is a valid attribute code, and is the active path when Elasticsearchis unavailable (DB fallback).
Changes:
ProductDataGrid::processRequestedSorting()andsetElasticSort(): coerce thesort direction to a strict
asc/descallowlist before it is used.ProductDataGridSortInjectionTest(Pest) andtests/08-security/product-sort-injection.spec.js(Playwright, live app).tests/e2e-pw/utils/login.jsto authenticate over HTTP so global-setupproduces a valid storageState (the DOM login failed silently in headless runs).
How To Test This?
/admin/catalog/products?sort[column]=name&sort[order]=asc,(SELECT CASE WHEN (1=1) THEN name ELSE id END FROM admins LIMIT 1)→ the direction is coerced to a safe keyword; the response is 200 with valid JSON
(no SQL error, no injection). Normal
asc/descstill works.vendor/bin/pest packages/Webkul/Admin/tests/Feature/Catalog/ProductDataGridSortInjectionTest.php→ 3 passed.npx playwright test tests/08-security/product-sort-injection.spec.js→ 2 passed.Documentation
Branch Selection
Pint
Tailwind Reordering
Tests