Magento – Applying Security Patches on Windows

As an addendum to the previous post, I wanted to add Windows specific information.

I use Cygwin to apply Magento patches. Make sure to configure Cygwin to install the patch binary.

Now, you’re going to have to make a small edit to the .sh file. Look for the second occurrence of PATCH_BIN – it should look something like this:

PATCH_APPLY_REVERT_RESULT=`$SED_BIN -e '1,/^__PATCHFILE_FOLLOWS__$/d' "$CURRENT_DIR""$BASE_NAME" | $PATCH_BIN $DRY_RUN_FLAG $REVERT_FLAG -p0`

You’ll want to surround $PATCH_BIN with double quotes – that’s because, chances are, the location of Cygwin is going to contain spaces, which is going to cause issues when executed in the command line. So, change it to look something like this:

PATCH_APPLY_REVERT_RESULT=`$SED_BIN -e '1,/^__PATCHFILE_FOLLOWS__$/d' "$CURRENT_DIR""$BASE_NAME" | "$PATCH_BIN" $DRY_RUN_FLAG $REVERT_FLAG -p0`

Other than that, you should be able to run the patch as detailed in the previous post.

Magento – Applying Security Patches and Fixing Conflicts

As you are probably well aware, Magento has been releasing a steady stream of security patches recently. I’m going to give a quick overview of applying them, and also dealing with possible conflicts.

Here are the official instructions for applying a patch. As you can see, they recommend running

sh patch-file-name.sh

However, as this Stack Overflow question illustrates, sometimes that won’t work. You might get something like

sh PATCH_SUPEE-5344_CE_1.8.0.0_v1-2015-02-10-08-10-38.sh -R
PATCH_SUPEE-5344_CE_1.8.0.0_v1-2015-02-10-08-10-38.sh: 14: PATCH_SUPEE-      5344_CE_1.8.0.0_v1-2015-02-10-08-10-38.sh: 127: not found
PATCH_SUPEE-5344_CE_1.8.0.0_v1-2015-02-10-08-10-38.sh: 14: PATCH_SUPEE-5344_CE_1.8.0.0_v1-2015-02-10-08-10-38.sh: 127: not found
PATCH_SUPEE-5344_CE_1.8.0.0_v1-2015-02-10-08-10-38.sh: 25: PATCH_SUPEE-5344_CE_1.8.0.0_v1-2015-02-10-08-10-38.sh: 0: not found
Checking if patch can be applied/reverted successfully...
Patch was applied/reverted successfully.

The two most highly rated answers are correct – there are bash specific lines in the patch file, and the default system shell may not be bash. In Ubuntu 6.10+, they have changed /bin/sh to dash. To get around this, make the patch file executable, and then run the patch file directly, without sh. That is,

chmod +x patch-file-name.sh
./patch-file-name.sh

Change patch-file-name.sh to the actual name of your file.

If all goes well, then you should see the following messages:

$ ./patch-file-name.sh
Checking if patch can be applied/reverted successfully...
Patch was applied/reverted successfully.

If it was not successful, you’ll see an exhaustive list what changes were applied, and which failed. Usually this happens when you have local changes of a file, or in one of our client’s cases, when Magento did not provide a patch specifically for their version. In that case, you might get something like this:

$ ./PATCH_SUPEE-5994_CE_1.4.1.0_v1-2015-05-15-04-33-58.sh
Checking if patch can be applied/reverted successfully...
ERROR: Patch can't be applied/reverted successfully.
 
patching file app/code/core/Mage/Core/Controller/Varien/Router/Admin.php
Hunk #1 succeeded at 93 with fuzz 2 (offset -2 lines).
patching file app/code/core/Mage/Core/Controller/Varien/Router/Standard.php
Hunk #1 succeeded at 206 (offset 1 line).
Hunk #2 succeeded at 271 (offset -8 lines).
Hunk #3 succeeded at 307 (offset -8 lines).
patching file app/code/core/Mage/Customer/Model/Customer.php
patching file app/code/core/Mage/Dataflow/Model/Convert/Parser/Csv.php
patching file app/code/core/Mage/Install/Controller/Router/Install.php
patching file app/code/core/Mage/Install/etc/config.xml
can't find file to patch at input line 185
Perhaps you used the wrong -p or --strip option?
The text leading up to this was:
--------------------------
|diff --git app/code/core/Mage/Sales/controllers/Recurring/ProfileController.php app/code/core/Mage/Sales/controllers/Recurring/ProfileController.php
|index 95c66dc..6f5ed5d 100644
|--- app/code/core/Mage/Sales/controllers/Recurring/ProfileController.php
|+++ app/code/core/Mage/Sales/controllers/Recurring/ProfileController.php
--------------------------
File to patch:
Skip this patch? [y]
Skipping patch.
1 out of 1 hunk ignored
patching file lib/PEAR/PEAR/PEAR.php
patching file lib/PEAR/PEAR/PEAR5.php
patching file lib/Varien/Io/File.php
Hunk #1 succeeded at 224 (offset -2 lines).

In this case, look for the line around app/code/core/Mage/Sales/controllers/Recurring/ProfileController.php. For this example, that file didn’t exist in the client’s version, so I edited the .sh file and removed the entire block, starting from diff --git app/code/core/Mage/Sales/controllers/Recurring/ProfileController.php app/code/core/Mage/Sales to the line just before the next diff.

If it’s a specific hunk that’s misbehaving, you can apply the change manually and then remove the hunk from the .sh file. It should be fairly easy to tell – lines starting with a + are new lines, lines with - are removed, and lines with neither stay the same. Look for the lines with neither +/- as a reference point, and add/remove lines accordingly. To remove the hunk, remove the hunk starting with @@ to just before the next line starting with @@ For example, in the below snippet, if I wanted to remove hunk #2, I would remove lines 16-33.

diff --git app/code/core/Mage/Core/Controller/Varien/Router/Standard.php app/code/core/Mage/Core/Controller/Varien/Router/Standard.php
index efaf186..42c2fe6 100644
--- app/code/core/Mage/Core/Controller/Varien/Router/Standard.php
+++ app/code/core/Mage/Core/Controller/Varien/Router/Standard.php
@@ -205,6 +205,10 @@ class Mage_Core_Controller_Varien_Router_Standard extends Mage_Core_Controller_V
             // instantiate controller class
             $controllerInstance = Mage::getControllerInstance($controllerClassName, $request, $front->getResponse());
 
+            if (!$this->_validateControllerInstance($controllerInstance)) {
+                continue;
+            }
+
             if (!$controllerInstance->hasAction($action)) {
                 continue;
             }
@@ -275,6 +279,17 @@ class Mage_Core_Controller_Varien_Router_Standard extends Mage_Core_Controller_V
     }
 
     /**
+     * Check if current controller instance is allowed in current router.
+     * 
+     * @param Mage_Core_Controller_Varien_Action $controllerInstance
+     * @return boolean
+     */
+    protected function _validateControllerInstance($controllerInstance)
+    {
+        return $controllerInstance instanceof Mage_Core_Controller_Front_Action;
+    }
+
+    /**
      * Generating and validating class file name,
      * class and if evrything ok do include if needed and return of class name
      *
@@ -300,7 +315,6 @@ class Mage_Core_Controller_Varien_Router_Standard extends Mage_Core_Controller_V
         return $controllerClassName;
     }
 
-
     /**
      * @deprecated
      * @see _includeControllerClass()

Finally, after you’ve run the patch script successfully, you might need to reset the owner/group of your files back to your web server user – otherwise the patched files will belong to the user you’re running the .sh file as. Quoting from the official docs,

a. Find the web server user: ps -o "user group command" -C httpd,apache2
The value in the USER column is the web server user name.
Typically, the Apache web server user on CentOS is apache and the Apache web server user on Ubuntu is www-data.
b. As a user with root privileges, enter the following command from the Magento installation directory:

chown -R web-server-user-name .

For example, on Ubuntu where Apache usually runs as www-data, enter

chown -R www-data .

Good luck in your patch adventures!