In which this humble website acquires 50% more unicorn…

Posted on January 15, 2017 in IT • 5 min read

Remy Porter

Remy Porter

This is the face of a man who clearly likes unicorns very very much…

Sometimes you see something out there in the world and you’ve just got to have it. Or emulate it. You want to do that thing because it was such a damn good idea. Such was the case last week when, as I sometimes do, I was indulging in a read through The Daily WTF. A website devoted to the more groan-inducing aspects of computer technology, the day’s post happened to be by Remy Porter and would immediately cause me to begin wondering “how can I do this?”

Now I’m not going to tell you WHERE in said post I found this, but suffice it to say that Remy has cleverly hidden an easter egg in his post that makes calls to a rather magical JavaScript library called Cornify.js. Cornify has the rather fantastic property that, when activated, it randomly covers the target in magical glittery unicorns and rainbows. This appeals greatly to my sensibilities of 1) having fun and b) not taking life too seriously.

Where the ops professional steps in

Now if you know any IT operations professionals you’ll know that the one thing that we hate is ‘yet another thing to maintain’. We would much rather have customizations that bolt on to the side of existing solutions rather than develop and maintain yet another project. And so it was in my attempt to bolt unicorns onto the side of this blog.

Cornify logo

Cornify logo

whereas this is the face of a unicorn…

I have an existing site theme that I’ve built off, you see, called Flex. It’s my favorite, and I love it. I love it because while it’s somewhat pluggable and configurable, I’m neither required nor able to futz with it too much, or likewise to completely own its upkeep. Yet here I was trying to add something (in this case shiny pink unicorns) that the author had definitely not planned for.

I keep Flex in a git submodule, and when I update my blog it rebuilds with a fresh pull of that submodule. ensuring that everything is up to date and tidy. GitLab CI, docker, all that. Which means that my customizations can’t simply be of the “write over the file” sort. In the case of modifications to CSS the author of Flex had been more than considerate, giving me the ability to source a file in which to place my many tweaks. But in the case of additional JS, well, he’d been not so kind. I should probably open a bug for that…

Return of the Jinja warrior

Now it just so happens that all of the actual theming and page-building legwork that the Pelican static blogging system provides is actually performed by the Jinja templating engine. Jinja takes a file with Jinja statments inside and performs actions on those statements. These actions render the finished file into the actual output that you’re looking for. Take, for exceedingly relevant example, this chunk of jinja template which defines the sidebar div for the page you are currently reading!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<div>
  <a href="{{ SITEURL }}">
    {% if SITELOGO %}
    <img src="{{ SITELOGO }}" alt="{{ SITETITLE }}" title="{{ SITETITLE }}">
    {% else %}
    <img src="{{ SITEURL }}/{{ THEME_STATIC_DIR }}/img/profile.png" alt="{{ SITETITLE }}" title="{{ SITETITLE }}">
    {% endif %}
  </a>
  <h1><a href="{{ SITEURL }}">{{ SITETITLE }}</a></h1>

  {% if SITESUBTITLE %}<p>{{ SITESUBTITLE }}</p>{% endif %}

  {% if pages or LINKS %}
  <nav>
    <ul class="list">
      {% if not PAGES_SORT_ATTRIBUTE -%}
          {% set PAGES_SORT_ATTRIBUTE = 'title' %}
      {%- endif %}
      {% for page in pages|sort(attribute=PAGES_SORT_ATTRIBUTE) %}
      <li><a href="{{ SITEURL }}/{{ page.url }}#{{ page.slug }}">{{ page.title }}</a></li>
      {% endfor %}

      {% for name, link in LINKS %}
      <li><a href="{{ link }}" target="_blank">{{ name }}</a></li>
      {% endfor %}
    </ul>
  </nav>
  {% endif %}

  <ul class="social">
    {% for name, link in SOCIAL %}
    <li><a class="sc-{{ name }}" href="{{ link }}" target="_blank"><i class="fa fa-{{ name }}"></i></a></li>
    {% endfor %}
  </ul>
</div>

Now as you can see Jinja has the power of looping constructs, and within one of those looping constructs is the definition that creates the named links on the left sidebar. Specifically…

{% for name, link in LINKS %}
<li><a href="{{ link }}" target="_blank">{{ name }}</a></li>
{% endfor %}

Now for those of you with HTML experience, this is an anchor tag with a href attribute, or what is commonly known as a hyperlink. Surrounding it is a Jinja for loop that iterates over a python dictionary (the equivalent of an array or hash in other languages.). It’s using a tuple unpacking syntax which will cause the first item of the tuple to be injected into the variable name and the second item of the tuple to be injected into the variable link. At present the contents of that dictionary look like this…

1
2
3
4
LINKS = (
  ('moar magick<script type="text/javascript" src="https://www.cornify.com/js/cornify.js"></script>',
   '#" onclick="cornify_add();return false;'),
)

The ugly truth

Are you catching what’s going on here? Jinja is substituting the string in line 2 for the link variable in the Jinja template, and the string in line 3 for the name attribute. This creates HTML that looks like this…

<a href="#" onclick="cornify_add();return false;" target="_blank">moar magick<script type="text/javascript" src="https://www.cornify.com/js/cornify.js"></script></a>

With a clever usage of quotation we’re essentially performing command injection here, but for a very benign use case (making software do a thing we want instead of the thing it was designed for). And the end result?

cornified site

This site, fully cornified

whereas this is the face of madness!

According to the W3C HTML Validator, this is valid HTML. Also it works. Also I’ve made no out-of tree modifications to upstream code, which means I don’t have to add either complex or brittle patching code while I wait for the author of the theme to make this option cleaner. This is a non-idealistic but highly SUSTAINABLE solution to a problem.

Why are we talking about this? This is dumb

The internet is littered with abandoned customizations to existing software projects for what often amounts to one or two line alterations. Often these alterations wouldn’t be necessary but for a little creativity on the part of the software’s users. Much like There I Fixed It, one can never treat one’s tools with too much sanctity. Don’t be afraid to hack, to use your wrench as a screwdriver if it meets your needs and your usage scenario. After all, the machine before you is a tool, designed to do your bidding, not the bidding of someone else.

Also…unicorns!

So do yourself a favor, and click the moar magic button today!