Published on 2017-01-15 14:25 by Ritz
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.
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!
<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…
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?
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!
Written by Ritz
← Back to post