Sanitize SVGs in WordPress

So my plugin Safe SVG has just been accepted into the WordPress plugin directory. Whilst mainly a proof of concept, I’m hoping that this plugin will help convince the core team that SVGs, with the right sanitization should become part of core.

My major argument for allowing SVGs in core with sanitization is that there are currently 128 other SVG upload plugins in the plugin directory (source), plus the likes of the posts on blogs like CSS Tricks such as this one that all show you how to allow SVG uploads in WordPress, but don’t address or sometimes even mention the massive security risks that come with allowing users to upload SVGs.

SVGs should be considered as standalone XML applications, there are a huge number of vulnerabilities that can be attacked. For example, some of the easiest to implement are XXE attacks and the Billion Laughs attack. Safe SVG nullifies these attacks by removing the DOCTYPE from the SVG file, something so simple yet overlooked by most other plugins.

Safe SVG also protects against XSS attacks embedded within the XML file by defining a strict whitelist of elements and attributes allowed within the SVG, anything not on these whitelists are removed. Whilst this may break some Javascript powered SVG animations, I feel that is a small price to pay for peace of mind when it comes to your users security.

Probably the best way to learn more about Safe SVG is to go and download it from the WordPress plugin directory and take a look through the code, alternatively you can see the library it’s built upon on Github. If you find a bypass or have any suggestions on how to improve this plugin or the underlying library, please don’t hesitate to contact me and let me know your thoughts.

Safe SVG for WordPress

After a lot of testing of svg-sanitizer I’ve finally decided to make a WordPress plugin for it. This is more of a PoC to show that it can be done.

Once installed, the plugin will hook into the uploads and automatically sanitize any SVGs that you upload.

I’ll update this post when it’s on the WordPress directory but for now, here’s the download:

Click here to download

WordPress InnoDB Issues

I’ve been working with a lot of WordPress plugins recently and have come across a bug that had me stumped for a while, therefore, I thought it’d be worth sharing it here.

After installing and activating a plugin that created new database tables, I realised that the tables had not been installed. I contacted the plugin support who suggested re-installing WordPress. Reluctantly I did this but it didn’t help at all, I therefore decided I’d dig into the issue and see if I could track it down.

A bit of work and a few var_dump()‘s later I found that there as an error with the database creation SQL (below)

CREATE TABLE IF NOT EXISTS wp_domain_mapping (
    id BIGINT NOT NULL AUTO_INCREMENT,
    blog_id BIGINT NOT NULL,
    domain VARCHAR(255) NOT NULL,
    active TINYINT DEFAULT 1,
    PRIMARY KEY (id),
    KEY blog_id (blog_id, domain, active)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci

I ran the query manually and realised it was returning the error Specified key was too long; max key length is 1000 bytes. I’d never seen this error before, so I dug into it a little more, this page informed me that InnoDB has a maximum key size of 1000 bytes, unless the innodb_large_prefix flag is set in MySQL in which case the maximum size is 3072 bytes.

That said, I found the following document where I could check the storage requirements of each field type. From this, I can tell that the BIGINT field, blog_id is 8 bytes, the TINYINT field, active is 1 byte and as UTF8 needs 3 bytes per character, the domain field is 255 * 3 bytes or 765 bytes. This gives us a total key length of 774 bytes.

It was then that I realised that I wasn’t using standard UTF8 but utf8mb4, which WordPress has set as it’s default character set as of 4.2. utf8mb4 requires 4 bytes per character and therefore the domain field calculation was wrong and was actually 255 * 4 bytes or 1,020 bytes in length. This is now an issue as our key is bigger than the size allowed by InnoDB hence the issue creating the tables.

Now as far as I can see, there is only one real way of getting around this and it’s the method WordPress has chosen, reducing the field length from 255 chars to something less, 247 in this case.

Now I know about this it shouldn’t be much of an issue, but I wonder how many plugin developers aren’t going to realise this has changed and are going to run into this. I foresee it being a major pain for a few months.

Anyway, onwards and upwards now!

Cheers for now!

Adding zoom level to Advanced Custom Fields maps.

We use Elliot Condon’s Advanced Custom Fields extensively in WordPress at work and I must say, it’s one of my favourite WordPress plugins ever made. That said, sometimes people ask for things that I just can’t work out how to do with the native plugin.

Today a client asked me to allow them to set their own zoom distance on their maps. We’re using the great ACF map field for all the maps so I thought, “Hey, that’s fine, I’ll just go and enable that option”, only to find that there isn’t an option for this. I did some digging around on Google and stumbled across a support thread that asks exactly what I wanted to know.

Whilst there was an answer in the thread, it required me to change the plugin files, something I plain up refuse to do. The last thing I want is for somebody to update their plugins at some time in the future and the whole thing to fall apart. No, I had to find a way of doing this without hacking into any other files.

I came up with the idea that I’ll use a separate field and the client can set their zoom level in there. Whilst that worked fine for me as I know the zoom levels, it wouldn’t be too great for the client, who wants to just be able to zoom in on the map and have that set. After reading through the code samples in the aforementioned support thread, it hit me that I should be able to bind to the zoom_change event in the Google Maps API and then output the value into the second field for use on the front end.

Upon first try, I ran into issues with google not being defined. This was down to the map not being loaded when my javascript was fired. I tried firing my function on window.load() but I still had the same issue. Somehow I had to delay the function until the Google API had loaded.

I ended up doing this by using a setInterval() on a 500ms interval to call a function that checks if typeof google !== 'undefined'. If this returned true, I could then cancel the interval and bind to the maps API. The second problem I ran into, was that I wasn’t sure how to access the ACF maps object. After looking around the ACF plugin file, I worked out that it’s set in js/input.js under the acf.fields.google_map.map object. I added this to my maps callback and found out that it wasn’t yet defined. This was then added to the previous variable check function and hey presto, it worked. Now it was just a case of linking the fields up, the easy bit.

Below are the files that I used. adminMaps.js handles the heavy lifting and setting of the fields whereas the snippet from my functions.php just makes sure that the JS file is only loaded on post.php in the admin area.

To use this, all you’ll have to change is the selector for the second field, on lines 10 and 18 of adminMaps.js, it’s currently set to #acf-field-zoom_level the ID of my field.

You can also access the full Gist here.

P.S. As a bonus, line 13 of the JS file will disable the scroll to zoom in the admin area!

WordPress Plugin Development

A few weeks ago I was commissioned to create a WordPress plugin for a client. This plugin needed to take a feed on news from my clients website and display it on their clients dashboards.

After a few searches of the WordPress directory, I couldn’t find anything suitable, to either use as as a complete tool, or as a base top develop on top of. As such, I decided that learning the basics of WordPress plugin development couldn’t be a bad thing. After all, I make my living developing websites and a large proportion of that is now WordPress sites.

After reading a few tutorials (One, Two, Three, Four) I managed to knock together an admin plugin to create a custom post type and a client plugin to read a JSON feed of the custom post type, into a widget on the dashboard. This was alright and worked for my client but I realised that there was still a whole in the WordPress directory for plugins to do this. This is when I decided that I’d re-write the plugins and publish them in the WordPress directory.

I played around a bit and seemed to spend a lot of time on Stackoverflow researching the best practices of plugin design. In the end, I went with two OOP plugins. Again, one for the admin side, this time with a settings menu, letting you choose how many posts to show to your clients and the name that will show up on the dashboard widget:

screenshot-1

and also one for the client side. This has a settings menu allowing you to point the request at your parent site, supplying the feed!

screenshot-1

Below is the widget on a test dashboard:

screenshot-2

 

Although fairly simple, I feel it’s taught me a fair bit about plugin development and has allowed me to progress my WordPress knowledge, something I know I can always improve upon.

For anyone that’s wondering, you can find the Admin plugin at: https://wordpress.org/plugins/dashboard-custom-feed-admin/  and I will update this post with the client side plugin as soon as it’s through review and into the WordPress directory here is the client plugin https://wordpress.org/plugins/dashboard-custom-feed/.