🚀 OpenMage 21 is the next step in the evolution of our beloved M1 platform. 🚀
📢🔔 While V21 introduces breaking changes that require careful consideration during the upgrade process, these long-overdue modifications will ultimately bring significant benefits to your stores. In the long run, you can expect enhancements in performance, security, SEO scores, and more, making the transition worthwhile. 📢🔔
Changes you must know and that you have to check one by one
Introduced in beta3
- OM 21.0.0-beta3 is based on OM 20.19.0, including all of its security fixes (see Security below) and its behaviour changes. Read the v20.19.0 upgrade notes as well as this list.
- The contact form's own form-key switch from 20.x is not carried over: the unified CSRF setting from beta1 applies to the contact form.
Mage_Catalog_Model_Product::formatUrlKey()andMage_Catalog_Model_Category::formatUrlKey()no longer take a$localeargument (20.x URL-key API, using the Symfony slugger).- Fixed in this release:
- Limited grid and report exports (
getCsv()/getExcel()with a file name and limit) no longer throw aTypeError. - Cached blocks no longer hand one visitor's form key to the next. Before, the footer newsletter form failed silently for most visitors.
- Removed a leftover Centinel override in
Mage_PaypalUk_Model_Directthat is a fatal error on PHP 8.3+.
- Limited grid and report exports (
- Locked laminas packages are updated for the PHP 8.2 minimum.
Introduced in beta2
- OM 21.0.0-beta2 is based on OM 20.10.0
- Removed deprecated global translate function
__() - SOAP APIs: Removed all imports from schemas.xmlsoap.org
Introduced in beta1
- PHP8.2 is now the minimum required version for OM v21
- Added form key validation to Contacts form
Checkapp/design/frontend/base/default/template/contacts/form.phtmlin your theme and be sure that you've<?php echo $this->getBlockHtml('formkey') ?>under the<formopening tag, as shown here. - Unified CSRF configuration
If you enabled CSRF inSystem -> Configuration -> System(which you should!) then it will be enabled for all your forms and the checkout. - Removed double span element from HTML buttons
Be sure to check your CSS styles for the buttons in your frontend theme, you may need to adjust them slightly. - Removed all deprecated
_Mysql4_classes
This will break many extremely old modules but it was long overdue and allowed us to remove 500+ obsolete files from the core. We provide theshell/rename-mysql4-class-to-resource.phpmigration script to fix the old modules, you will have to run it but make sure you'll do it in a test environment and thoroughly test everything before publishing to production. - Removed
decorate*()js functions
Check the phtml files of your custom themes for javascript code callingdecorate*()(decorateList, decorateTable, decorateGeneric etc) and remove those calls since they will generate javascript errors on your browser's console. - Removed "admin routing compatibility mode" for extensions
The new admin routing mode was implemented in Magento 1.4 for security reasons, together with "compatibility mode" (in order to allow some time to developers to update their modules). After these many years we are removing the compatibility mode and all extensions will have to follow the new admin routing mode or they will not appear in the backend. - Removed
scriptaculous/dragdrop.jsfrom frontend
Changes you want to know about
Introduced in beta2
- RWD theme: Added explicit width/height for product thumbnails in product list template
- RWD theme: Removed ancient normalize.css
Introduced in beta1
Changes that probably won't affect you
Introduced in2
- Rewrote
js/varien/menu.jswithout prototypejs but no longer linked in thebasetheme - Rewrote
js/mage/adminhtml/accordion.jswithout prototypejs - Rewrote
js/mage/adminhtml/input-counter.jswithout prototypejs - Rewrote
payment.jswithout prototypejs - Removed unused files from
app/code/core/Mage/Adminhtml/Block/Catalog/Category/Tab - Removed unused file
js/varien/telephone.js - Removed obsolete module Mage_Centinel
- Removed deprecated module DirectPost from Authorize.net payment method
- Removed ccard.js in favor of public domain code
- Removed deprecated
escape()fromcookies.js
Introduced in beta1
- RWD theme: removed
enquire.jsand converted to window.matchMedia - RWD theme: converted default logos to SVG (and removed some Magento names)
- Removed onmouseover/onmouseout from adminhtml menu
- Removed auto_detect_line_endings in Varien_Io_File
- Fixed implementation of SessionHandlerInterface in Mage_Core_Model_Resource_Session
- Rewrote
js/varien/weee.jswithout prototypejs - Rewrote
js/varien/accordion.jswithout prototypejs - Rewrote
js/mage/captcha.jspwithout prototypejs - Removed unused file
js/mage/adminhtml/scrollbar.js - Removed unused file
js/mage/adminhtml/magento-all.js - Removed unused file
js/mage/adminhtml/image.js - Removed deprecated and unused files from
lib/Mage - Rewrote
js/mage/adminhtml/hash.jsusing native javascript - Rewrote
js/mage/adminhtml/variables.jswithout prototypejs - Rewrote
js/mage/translate.jswithout prototypejs
Security
v21.0.0-beta3 carries every fix in OpenMage LTS v20.19.0. This release fixes 34 security issues: 16 are published with CVE identifiers and 18 as GitHub Security Advisories only. Upgrade as soon as possible. All 34 are fixed in v20.19.0. v20.18.2 is a security-only patch release on top of v20.18.0 and fixes all of them except the three marked 20.19.0 only. Those three need behaviour or schema changes too large for a patch release. The 21.x pre-release line is fixed in v21.0.0-beta3.
Why there is no v20.18.1. An automatically generated release draft named "v20.18.1" sat on the Releases page for months. It was never tagged or released, and its contents were feature work, which now ships as v20.19.0. We skipped the 20.18.1 number deliberately, so nobody confuses the security patch with that old draft. v20.18.2 is the first patch release after v20.18.0.
Scores are CVSS 3.1. Where a vulnerable feature ships disabled, the advisory says so and gives the score for stores that have it enabled.
Advisories with a CVE
| Advisory | Issue | Severity | CVE | Fixed in |
|---|---|---|---|---|
| GHSA-grrq-9gq2-vm55 | CSRF in OAuth authorization grants persistent admin or customer REST API tokens | Critical 9.0 | requested | 20.18.2, 20.19.0 |
| GHSA-4fxg-469f-r6fc | Protected-extension blocklist omits phar/php8/phps, enabling guest file-upload RCE | High 8.1 | requested | 20.18.2, 20.19.0 |
| GHSA-7j66-6mw7-rgfx | Predictable customer confirmation key and password-reset tokens allow account takeover | High 8.1 | requested | 20.18.2, 20.19.0 |
| GHSA-j9c5-pcvx-jx9x | Stored XSS in RSS feeds served as text/html lets a guest run script in an administrator's browser | High 8.0 (9.3 with the review feed enabled) | requested | 20.18.2, 20.19.0 |
| GHSA-mffc-h5pf-6f42 | OAuth callback URL allowlist bypass leaks admin or customer OAuth verifier | High 7.7 | requested | 20.18.2, 20.19.0 |
| GHSA-3w23-mpwf-q79h | Authenticated RCE via unsafe unserialize() of CSP configuration | High 7.2 | requested | 20.18.2, 20.19.0 |
| GHSA-57qq-p497-7grq | Authenticated RCE via absolute-path file write in product image APIs | High 7.2 | requested | 20.18.2, 20.19.0 |
| GHSA-v2qw-3rm9-qx3p | Default XML namespace bypasses the layout update validator (file read/write by a catalog-only admin) | High 7.2 | requested | 20.18.2, 20.19.0 |
| GHSA-8v83-5w2r-ghrp | Authenticated RCE via unsafe unserialize() in Varien_Convert_Parser_Serialize | High 7.2 | requested | 20.18.2, 20.19.0 |
| GHSA-989j-pjxc-9hgp | Dataflow HTTP cURL adapter reads local files and internal URLs (SSRF, local file disclosure) | Medium 6.8 | requested | 20.18.2, 20.19.0 |
| GHSA-68jq-pfh7-9pjx | SSRF in downloadable product URL links and samples | Medium 6.8 | requested | 20.18.2*, 20.19.0 |
| GHSA-75mq-cvpr-g7wq | Reflected XSS via unescaped session messages | Medium 6.1 | requested | 20.18.2, 20.19.0 |
| GHSA-w5fm-m59p-w9q5 | Cross-customer custom-option file disclosure | Medium 5.9 | requested | 20.18.2, 20.19.0 |
| GHSA-2p72-3v6m-9p2h | Custom-option file download path traversal to arbitrary file read | Medium 5.3 | requested | 20.18.2, 20.19.0 |
| GHSA-3hh8-xg45-mwrr | Forgeable order-status RSS URL key exposes other customers' orders (RSS off by default) | Medium 5.3 | requested | 20.18.2, 20.19.0 |
| GHSA-23v7-qrrp-4m96 | SOAP API trusts the Host header for its WSDL URL (blind SSRF, WSDL injection) | Medium 5.3 | requested | 20.18.2, 20.19.0 |
* In 20.18.2, downloadable links on private networks are still allowed by default, to avoid breaking existing stores. See Behaviour changes.
Advisories without a CVE
These are published so you can track them. Each one either needs an administrator account and crosses no authority boundary, or has low impact.
| Advisory | Issue | Severity | Fixed in |
|---|---|---|---|
| GHSA-p87f-mr5h-8ww9 | Unsafe unserialize() without class restriction in recurring profile and date option | Medium 6.6 | 20.18.2, 20.19.0 |
| GHSA-m858-x59w-rmp5 | Admin-to-admin stored XSS (Dataflow status, tax rate title, OAuth consumer name, PayPal certificate field) | Medium 6.1 | 20.18.2, 20.19.0 |
| GHSA-rrhg-c635-fmrm | API access survives user deactivation (SOAP/XML-RPC sessions and REST OAuth tokens) | Medium 5.9 | 20.18.2, 20.19.0 |
| GHSA-rmw8-7h79-p386 | Free payment method accepted on orders with a non-zero total (cart API) | Medium 4.4 (5.9 on API/headless checkouts) | 20.18.2, 20.19.0 |
| GHSA-rqc2-p2h7-6hw9 | Guest order lookup rate limiter never throttles a request | Medium 5.3 | 20.18.2, 20.19.0 |
| GHSA-94p5-6j66-c9j6 | Email template getConfig directive discloses encrypted configuration secrets to restricted admins | Medium 4.9 | 20.18.2, 20.19.0 |
| GHSA-v7mx-rv47-452m | Guest SVG upload via product custom option runs script in the store origin | Medium 4.7 | 20.18.2, 20.19.0 |
| GHSA-28hq-mq6h-fh2x | Stored XSS via Dataflow batch import row errors | Medium 4.7 | 20.18.2, 20.19.0 |
| GHSA-6pvw-8mv9-r8vr | Reflected XSS on error pages via unescaped SCRIPT_NAME-derived URLs | Medium 4.7 | 20.18.2, 20.19.0 |
| GHSA-9783-jfj5-gfpv | Missing CSRF protection on customer account actions | Medium 4.3 | 20.18.2, 20.19.0 |
| GHSA-wmwx-442r-jc88 | Order-status RSS feed discloses internal invoice, shipment and credit-memo comments | Medium 4.3 | 20.18.2, 20.19.0 |
| GHSA-7vww-whgh-98cf | WYSIWYG image directive endpoint allows SSRF, media path traversal and path disclosure | Medium 4.1 | 20.18.2, 20.19.0 |
| GHSA-wgf8-gh63-gfqf | Insufficient granular ACL enforcement in admin controllers | Low 3.8 | 20.18.2, 20.19.0 |
| GHSA-5rg7-22cv-c9q6 | Race condition allows coupon usage limits to be exceeded | Low 3.7 | 20.19.0 only |
| GHSA-hfqq-c3qm-38x8 | Admin-planted stored XSS via product name (storefront MSRP popup, Qty Increments notice) | Low 3.5 | 20.18.2, 20.19.0 |
| GHSA-2jfg-wx75-5cc8 | CSRF in PayPal cancel/decline return handlers | Low 3.1 | 20.19.0 only |
| GHSA-fqq6-76qg-2r96 | {{layout}} template directive bypasses the block whitelist
| Low 2.7 | 20.19.0 only |
| GHSA-w949-chmj-mx9m | Path traversal in WYSIWYG thumbnail endpoint reads images outside media/wysiwyg | Low 2.7 | 20.18.2, 20.19.0 |
Hardening
These are not vulnerabilities in a default install, but are fixed as defence in depth. Thank you to everyone who reported them.
Mage_Archive_Tarnow confines extracted entries to the target directory, skips symlinks that escape it, and drops setuid/setgid/sticky bits. No core code calls it. Thanks @lichoin.- The three
phar://guards are case-insensitive and catch nested stream wrappers. Thepharwrapper is already unregistered at bootstrap. Thankssubhashdasyam(HackerOne) and Bassem Tarek (@1xNANO). - Dataflow profiles refuse action and container types that are not Dataflow containers before constructing them, and stop with an error when an action names a method the container lacks. Thanks @hariprasanth9317-tech.
- Gift message To/From fields escape the customer's default name (self-XSS). Thanks @0x5t4l1n (
w4nn4d13on HackerOne). Varien_Simplexml_Config_Cache_Fileunserializes its.statfile withallowed_classes => falseand treats a tampered file as a stale cache.- A regression test keeps the HTMLPurifier pass of the MaliciousCode filter in place against unclosed-tag and slash-separated event-handler shapes. Thanks @Judx.
Behaviour changes and upgrade notes
Review these before you upgrade. Most need no action unless you run a custom theme, an extension that overrides the named templates or classes, or a restricted admin role.
Server configuration
- nginx: deny
/media/custom_options/. Addlocation /media/custom_options/ { deny all; }next to your existing rules for/media/customer/,/media/downloadable/and/media/import/. The upgrade does not touch your server config. Apache users get a newmedia/custom_options/.htaccess, so make sureAllowOverridelets it take effect. - Rate limiter behind a proxy or CDN. After a failed Orders and Returns lookup, the client IP is now refused for
system/rate_limit/timeframeseconds (default 30). If you run behind a load balancer, reverse proxy or CDN, list the real-client-IP header underglobal/remote_addr_headersinapp/etc/local.xml. Otherwise all visitors share one IP, and one failed lookup blocks everyone. Setsystem/rate_limit/activeto0to switch the limiter off.
Configuration to review
- Downloadable products: private networks. Link and sample URLs that resolve to loopback, link-local, cloud-metadata or reserved addresses stop working in both releases. In 20.19.0, private-network hosts (10/8, 172.16/12, 192.168/16, 100.64/10, fc00::/7) are also refused by default. In 20.18.2 they are still allowed by default. Go to System → Configuration → Catalog → Catalog → Downloadable Product Options, set Allow Link URLs on Private Networks to No, and list hosts you trust under Trusted Link URL Prefixes. A new hourly cron job,
downloadable_link_url_audit, and admin inbox messages list affected links. - Upload extensions.
php8,pharandphpsare refused wherever the protected-extension list applies. The protected list now has a fixed minimum (PHP variants,phtml,pht,phar,phps,pl,py,cgi,sh,asp,aspx,jsp,htaccess,shtml) that applies even if you saved your owngeneral/file/protected_extensions; you can add extensions but not remove these. A File custom option with no "Allowed File Extensions" list refuseshtm,html,svg,svgz,xhtml,xht,xml,xslandxsltuploads; list a type on the option to accept it, and keepmedia/custom_options/off the web if you do. - Dataflow URL fetch. An invalid or non-http(s)
uriin a cURL adapter now fails the profile. Hosts that resolve to loopback, link-local (cloud metadata) or other special-purpose addresses are refused, and redirects are not followed, so point profiles at the final URL. Private-network hosts stay allowed unless you set Allow Private Network Addresses to No. Hosts listed under System → Configuration → System → Dataflow URL Fetch → Allowed URL Prefixes skip the address check. If PHP runs with anhttp_proxyenvironment variable, the proxy resolves host names; restrict the proxy's destinations. - Email templates.
store.getConfig()in any letter case returns empty for encrypted settings and their parent nodes. If restricted admins have held System → Transactional Emails, consider rotating payment and carrier credentials.
Admin roles (granular ACL)
- Editing an order address now requires Sales → Orders → Actions → Edit.
- Editing an existing order requires Edit, not Create. Create-only roles lose this; Edit-only roles regain it, which fixes the regression in #2342.
- Each of these now needs its own permission: mass Cancel/Hold/Unhold, Void, report exports and detail pages, All Tags, REST role and REST attribute management, table-rate export, USPS tools, and tax rate import/export.
- Print All leaves out credit memos for roles without credit-memo access.
- Some narrowly scoped roles gain access they were wrongly denied: Bestsellers export, tax import, and the REST role Add button. Review roles that are not full-access after upgrading.
Themes and links (CSRF)
- Customer-account actions need a form key. These actions return 404 without one: billing agreement cancel, reorder (customer and guest), recurring profile cancel/suspend/activate/update, and customer OAuth token revoke/delete. Themes that use the stock block URL getters keep working. Custom code that builds these URLs itself must add
form_keyor POST with theformkeyblock. In 20.19.0 the base and rwd templates use POST forms; 20.18.2 changes no templates. - OAuth authorization buttons. The confirm actions require POST plus the form key, and a GET returns 404. If you override
oauth/authorize/button.phtmlorbutton-simple.phtml, submit withmethod="post"and include theformkeyblock. - OAuth callbacks. A consumer with a registered callback URL must use the same scheme, host and port, under the registered path.
- PayPal return URLs (20.19.0 only). Cancel and decline returns now carry a one-time
/nonce/<hex>/segment. Buyers who are at PayPal during the upgrade return without one, and their order stayspending_paymentuntil you cancel it. Extensions overridinggetStandardCheckoutFormFields(),_buildTokenRequest()or_buildFormUrlRequest()must keep the parent's nonce. {{layout}}directive (20.19.0 only). Only handles declared underglobal/template/email_layout_handlesare rendered. A module that uses its own handles in emails, CMS, widgets or newsletters must declare them, or that content renders empty and a log line is written.
Feeds
- RSS feed URLs are re-issued. Every order-status feed URL issued before the upgrade stops working, and customers get new signed links on their order page. This only matters if order-status RSS is enabled, which it isn't by default. All RSS responses are now
text/xmlwithnosniff, names in feed entries are HTML-escaped, and hidden invoice, shipment and credit-memo comments no longer appear in the customer order-status feed.
Other
- Layout updates are checked more strictly. Updates that contain
xmlns, callsetTemplate/setDataUsingMethodwith a different letter case or aClass::prefix, name a disallowed block throughclassor amodule/namealias, or put..in a template argument are rejected. CMS pages and widget instances are validated only on save, so after upgrading, searchcms_page.layout_update_xml,cms_page.custom_layout_update_xmlandcore_layout_update.xmlforxmlns(and for unusualmethod=values or..) and remove anything you did not put there. Product and category updates are dropped at render time. - Session messages are purified. Flash messages on the storefront and in the admin keep only simple markup (
a b br em i strong span p ul ol liand similar); other tags appear as text, andclass,styleandtargetattributes are removed. Check extensions that put buttons, forms, images or scripts in messages. The basecore/messages.phtmltemplate now prints the same purified output. - API access after deactivation. Deactivating a SOAP/XML-RPC API user or changing its API key now ends its open sessions immediately. REST requests with an OAuth token of a deleted or inactive admin user get HTTP 401, and deactivating an admin user revokes its OAuth tokens; after reactivating the account, re-enable them under System → Web Services → REST - OAuth Authorized Tokens.
- Customer confirmation keys. Accounts that registered before the upgrade and are still unconfirmed keep their old key. Consider removing stale unconfirmed accounts.
- Checkout. The Zero Subtotal Checkout method refuses an order whose grand total is not zero.
- Coupon usage limits (20.19.0 only). Uses per Coupon and both Uses per Customer limits are enforced atomically when the order is placed; an order that would exceed a limit fails with a message. Schema change:
salesrule_customergets(rule_id, customer_id)as its primary key and duplicate rows are merged (15–40 s on 2 million rows; run the upgrade in maintenance mode).salesrule_rule_save_before/_afterno longer fire when an order uses or cancels a rule; listen to the newsalesrule_usage_update_afterevent instead. - Free-shipping coupons. A rule or coupon that only grants free shipping now counts towards its Uses per Coupon and Uses per Customer limits. Before, those limits never applied to it, because the order carried no discount amount (#4757, fixed by #5554). Uses from before the upgrade were not recorded.
- Product image APIs (REST/SOAP). Image file names are reduced to
[A-Za-z0-9_.-], so spaces and slashes become underscores. (20.19.0 only)Varien_Io_File::write()throws when given an absolute target outside the directory opened withopen()/cd(). - Custom-option file downloads return 404 when the stored path lies outside
media/custom_options/, or when the file no longer matches its key. An admin following a wishlist file link from the customer edit page also gets 404. - SOAP WSDL. The WSDL and
soap:addressuse the configured store host unless the request'sHostmatches a configured store base URL. - Admin error output. A failed Dataflow profile run shows a generic error and logs the exception to
var/log/exception.log. - Template overrides. If your theme overrides any of these templates, apply the same escaping:
tax/rate/title.phtml,sales/order/totals/tax.phtml(admin),oauth/authorize/form/login*.phtml,system/convert/profile/process.phtml, the MSRP price templates, andcataloginventory/qtyincrements.phtml.
Thanks
We thank every researcher who reported these issues through GitHub and HackerOne. Each advisory names its reporters.
Full Changelog: v21.0.0-beta2...v21.0.0-beta3