February 1, 2018 Header


February 2018 Twenty-nine posts

from Instagram https://www.instagram.com/p/BfxDKsClHtI/ via IFTTT

Tonight @helenprejean inspired the crowd @cchsdons tonight. She says like Elie Wiesel she was called to share what she has witnessed. Passionate powerful speaker!

Color the Smiley

Over the years I have made a lot of little web experiments and geegaws. I’ve made hangman games and drawing programs and random text generators. At one place I worked I helped make a thing that would turn an uploaded photo of a face into a Terminator face!

I’ve made so many of these kind of things I don’t always remember I’ve made them. Enter the Smiley!

I remember, vaguely, building this. I don’t have a record of when I did. I include a link to my twitter feed but I can’t find a tweet about it. I joined twitter in 2006 so it could be anytime then. The code is the best source of clues:

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" type="text/javascript"></script>

The usage of jQuery 1.4.2 is the tell. Version 1.5 came out on January 31, 2011, and 1.4 was a year earlier. So 2010 is a good bet. 2010 had a lot of things going on. It happens to be the year my mother was so sick, and died. I think that’s why I never wrote about it.

But what about the code?

	$('#controls span').bind('click mouseover focus', function(){

		var fig = $('span.selected:lt(3)').text();
		var grd = $('span.selected:gt(2):lt(6)').text();
		$('div#wrapper, .smiley div.figure').css('background-color', '#' + fig);
		$('.smiley, .smiley div.ground').css('background-color', '#' + grd);
		location.hash = '#' + fig + '' + grd;
		return false;

	var colors = location.hash.split('#')[1];
	for(var i=0;i<colors.length;i++){
		$('div.segment div:eq('+i+') span:contains('+colors.substr(i,1)+')').trigger('click');

The code is not bad! It does the job, it uses idiomatic jQuery in a way I might today. I’d probably organize the whole thing differently now but it’s a fun little snippet. These days I might play with in such a way that you could share the silly smiley with people on social media. I might add a feature to turn it into an image. I might change the interaction a bit – I do have the numerical color value buttons respond to mouseover and click, so users on a phone or tablet don’t have a bad time. I also made it very narrow, also friendly to phones of the time.

Now, I can tell by the CSS I didn’t use SASS or any other CSS Preprocessor so that’s a bummer there. I also think I’d organize the code in a more MVC way so that the color model could be reused for something else.

Thanks for reading.

from Instagram https://www.instagram.com/p/BfrvT3NlBkU/ via IFTTT

Leah is cool.

I blogged for 17 years and all I got were these lousy Markov chains.

On Friday I wrote about how I turned 17 years of blog text into interesting sentences. I’ve also put that up on the web at lab.artlung.com/bloggingbot and now, with the help of Cheap Bots Done Quick (a free bot hosting service created by George Buckenham).

I turned that into a Twitter bot that tweets regularly. You can follow it at @BloggingBot.

I really like the code CBDQ uses: Tracery. It’s a straightforward, JavaScript-like syntax that’s kind of fun to write. It’s the brainchild of GalaxyKate.

"sentences": ["Here are videos of some of Leah's brothers and sisters.",
"Trying to get back on the horse, and I suppose I'm glad I don't get seasick.",
// 10,000 lines of pre-generated Markovify sentences
"If there are fewer, but still a long way to go. from Instagram IFTTTHere for kind of a joke as a band."],
"option": ["You asked for it!", "Here you go!", "Yes indeed!"],
"origin" : "#sentences#"

It’s free to host! Now, previously I’d written about @Ventcheck a bot I wrote at San Diego Code Kitchen in 2016.

BloggingBot can answer a reply made to it, because CBDQ has added that functionality. The reply Tracery code is nice too:

	"more please":"#option#\n\n#origin#\n\nThanks you for your kind attention. cc @artlung",
	".":"Hiya. I'm just a bot. But if you answer with \"more please\" then I'll tweet you again. :-)"

I submitted it to Mefi Projects yesterday. I look forward to having more projects to submit there. Metafilter is a great community.

So if you feel inclined, please feel free to follow @BloggingBot

I’m enjoying trying to write something technical every day. It’s a challenge that feels doable.

Thanks for your kind attention.

from Instagram https://www.instagram.com/p/Bfm03oyl6e-/ via IFTTT

So I wrote a bot this past week. It’s a twitter bot (@BloggingBot) & based on my 17 years blogging it makes sentences based on my words. It is surreal to see what it has generated. Because some of them sound like things I might have written. #markovchain #programming #twitterbot

from Instagram https://www.instagram.com/p/Bfmj_MMF4Bu/ via IFTTT

This guy—Billet(I can’t remember if that makes right)—is being fostered by my aunt & uncle. He’s nice.

Markov Chains are hilarious.

I’ve wanted to make Markov chain generated sentences based on this blog for a long while. Wednesday night, I did. It took me under an hour! Why have I avoided it thinking it’d be difficult? Probably I thought it’d take a long time and would be lame.


First, I exported the contents of my blog: Tools > Export

Out of that, you get a complete dump of your site’s content. This is a WordPress eXtended RSS file. What we care about are just two things from each post: the title and the content. To turn this XML into text I wrote a bit of PHP. It parses the <item> elements and then does a bit of string manipulation to remove all the HTML tags. It then dumps it into a file called “artlung.corpus.txt”.

  $xmlstr = file_get_contents("artlung.wordpress.2018-02-22.xml");
  $outputfilename = "artlung.corpus.txt";
  $blogcontent = new SimpleXMLElement($xmlstr);
  $all_text = array();
  $i = 0;
  foreach ($blogcontent->channel->item as $item) {
    $content = $item->children('content', true);
    $content = str_replace('>', "> ", $content);
    $content = str_replace('\n', " ", $content);
    $all_text[] = (strip_tags($item->children('content', true)));
    $all_text[] = ((string)$item->title);
  $str = implode("\n", $all_text);
  file_put_contents($outputfilename, $all_text);

(I ran this locally on my Mac since I have PHP installed. Did you know you can run a one-line PHP server? Just run php -S localhost:8000 and you have a local web server! I use it all the time. Read about it here.

Then I talked to my friend Kelly and asked her about what library I might use to accomplish this task. She pointed me to NLP (Natural Language Processing) libraries. I did a bit of googling and found Markovify: “a simple, extensible Markov chain generator.” It seems very well documented and the README on GitHub had easy install instructions. I already had python and pip installed and a terminal session with sudo pip install markovify did the trick.

Once I did that, I created a file “artlungmarkovify.py” and put this in it:

import markovify

# Get raw text as string.
with open("artlung.corpus.txt") as f:
    text = f.read()

# Build the model.
text_model = markovify.Text(text)

# Print five randomly-generated sentences
for i in range(10):

# Print three randomly-generated sentences of no more than 140 characters
for i in range(3):

Then I ran python artlungmakovify.py in terminal and BOOM! There’s some not-quite-Joe-Crawford blog sentences (every other sentence italicized to help distinguish the lines):

I expect she’ll be blogging about the interests of governance vs. conservative media in David Frum: Waterloo: I’ve been working at Yahoo has.
Still amazed we made together.
Nobody cares if you know it.
Immersed in his last name while he asked me that if life were these: Joe Crawford | Joe in LeahPeah’s GlassesNice picture, eh?world, meet olivia rex.
When I tend to ramble a bit, pulled nonnative plants, planted native ones.
Also, my feet and went with following the instructions on the NYTimes website Ted Koppel grills FEMA head Mike Brown.
Perhaps in expiating these exhibits we’ll actually find that when in 2004 and 2005.
Roth had a birthday card weighing one pound.

I then loaded up the file into BBEdit and did some cleanup. I removed duplicate lines, removed things like URLs. Sorted all the individual lines. I used BBEdit’s Markup > Utilities > Convert HTML to Text to undo the HTML entities that were there. I can see that a clean set of sentences are important for getting a good Markov result.

Now I have it generating lines like:

I think of the world of connections: Online social networks are powerful.
I left them in town.
This tension and disquietude sometimes spiral out of steak, chicken, pasta, and pretty much Nazis and haven’t caught on that beach, and the like.
People all over it.
I checked in at the cover here.
I don’t really care for that hair under your belt.
Last Friday, just 3 days to come.
Remembrances all over the weekend.
I’ve been blogging since before I was not particularly interested, but she generally agreed.
But it feels honest to do that, and I’m looking forward to with delight, fear, wonderment.
I’m going to tap my phones and tablets and e-readers shows us that it’s like I’m going to have never met her.
101 in 16 years for a logo, and right there for the stage.
As it is to say, the whole family is way out at subway stops.
My Grandfather retired and they don’t have an original design vinyl giant robot.
I’m doing lots of orange groves back here.
Clever folks will realize you can get to the rock line.
It’s been a few dozen people in this house in which he invited every passerby to lie in the surf, another from yesterday.
The fact that my increased notoriety brings more candy, I expect we won’t get to do serious work.
It will cost $1743 to fix the toilet which was leaking.I have posted in the meantime.A few weeks ago at an unexpected angle – young, white, female and with each player serving in turn.
Yes, San Diego Blog Meetup — Calling all Web Designers in San Diego very dearly.
Who else gets cool Swedish web developers take the train?
But the show must go to the car windows unrolled when the receiver to participate in a much less here.
It was a contest that year to year quickly.
If I finish a silly Flash game I want one of those old images.
It strikes me as a teen, working in Hollywood.

I love this. It’s entertaining silliness.

Now, I sent a few sentences to Leah and she said “it sounds a bit too much like President Trump”–which admittedly made me rather sad. But I can see that with a good corpus some rather interesting things might come out of it. My friend Kelly on reading some results said “It’s very interesting. Like a semantic word cloud.”

It really feels like this century will have more things like this. At some point computers will so good at mimicking us that we won’t be able to tell the difference. What that means for us as a culture? Hard to say.

from Instagram https://www.instagram.com/p/BfgyjyyFPHs/ via IFTTT

Limones 🍋

Suspicious Activity: “An admin user with the username backup was created outside of WordPress”

On 14 February 2018, WordFence alerted me to highly suspicious behavior. This is the format of the email I received from WordFence version 4.9.3:

This email was sent from your website “[REDACTED BLOG NAME]” by the Wordfence plugin.

Wordfence found the following new issues on “[REDACTED BLOG NAME]”.

Alert generated at Wednesday 14th of February 2018 at 12:07:50 PM

Critical Problems:

* An admin user with the username backup was created outside of WordPress.

NOTE: You are using the free version of Wordfence. Upgrade today:

Receive real-time Firewall and Scan engine rule updates for protection as threats emerge
Other advanced features like IP reputation monitoring, country blocking, an advanced comment spam filter and cell phone sign-in give you the best protection available
Remote, frequent and scheduled scans
Access to Premium Support
Discounts of up to 90% for multiyear and multi-license purchases
Click here to upgrade to Wordfence Premium:

This is suspicious on its own, but I wondered if it might be benign. Or at worst, maybe was a plugin gone bonkers. Or maybe my web host was doing something wonky. But strangely, when looking at the accounts in the database I noticed there was no nickname or email address associated with the account. WordPress requires both to create an account if you go through the normal way.

This email was sent from your website “[REDACTED BLOG NAME]” by the Wordfence plugin at Wednesday 14th of February 2018 at 08:28:38 AM

The Wordfence administrative URL for this site is: http://example.org/wp-admin/admin.php?page=Wordfence

A user with username “backup” who has administrator access signed in to your WordPress site.

User IP:
User hostname: ec2-35-183-24-90.ca-central-1.compute.amazonaws.com
User location: Montreal, Canada

…then WordFence alerted me this user logged in from an EC2 instance.

Then I got 5 more log-ins in the space of about 10 minutes to other sites I maintain. From the same computer. In my mind, this elevated “suspicious” to “probably malicious.” In that moment I was very glad for my MySQL backup processes that would allow me to go back a few days in time before this nonsense.

The behavior was so swift I wonder about whether it was automated, but logging in with saved credentials is not a terrible slow operation even for a person.

The lack of email address on the accounts implies, to me a concrete plan. No email address means they would have no way to reset their password but also no way for me to track them by email.

I knew I wanted to remove access for this user. So for each I needed to

    1. Change their password
    2. Log them out
    3. Add an email address I control for each account
    4. Change the SALT for the password encryption for each site.

So I did 1, 2, and 3. I used the standard WordPress user administration panel to change each password and log each out. I also added an email address using the gmail “+whatever” format:

Append a plus (“+”) sign and any combination of words or numbers after your email address. For example, if your name was hikingfan@gmail.com, you could send mail to hikingfan+friends@gmail.com or hikingfan+mailinglists@gmail.com.

I kept a list of the sites I was modifying as I went. I also inspected some of the other applications I have hosted to see if there was anything unusual there. Code injections or user creation, for example. I didn’t see anything.

Then I did step 4 for each of the sites. Wordress provides a “SALT CODE GENERATOR” at https://api.wordpress.org/secret-key/1.1/salt/ which gives you a code block suitable for adding to wp-config.php easily. What is the Salt? I let the WordPress documentation tell you:

In Version 2.6, three (3) security keys, AUTH_KEY, SECURE_AUTH_KEY, and LOGGED_IN_KEY, were added to ensure better encryption of information stored in the user’s cookies. These collectively replaced a single key introduced in Version 2.5.
In Version 2.7 a fourth key, NONCE_KEY, was added to this group. When each key was added, corresponding salts were added: AUTH_SALT, SECURE_AUTH_SALT, LOGGED_IN_SALT, and NONCE_SALT.

You don’t have to remember the keys, just make them long, random and complicated — or better yet, use the online generator. You can change these at any point in time to invalidate all existing cookies. This does mean that all users will have to login again.

A secret key makes your site harder to hack by adding random elements to the password.

In simple terms, a secret key is a password with elements that make it harder to generate enough options to break through your security barriers. A password like “password” or “test” is simple and easily broken. A random, long password which uses no dictionary words, such as “88a7da62429ba6ad3cb3c76a09641fc” would take a brute force attacker millions of hours to crack. A ‘salt‘ is used to further enhance the security of the generated result.

I’ve not seen any recurrence of the suspicious behavior and have not seen any attempted entry by any of the “backup” accounts created.

There’s no common themes or plugins (other than WordFence and WordPress and my own host) to link the intrusions but I’ll remain vigilant. When I wrote “WordFence is vital for self-hosted WordPress instances” the other day this is the kind of thing I was thinking of.

I reported the behavior to my web host, which they appreciated. I had blocked the IP address for all my sites from that host which they verified I did correctly. They also showed the logs of the logins and other than dates and times not much was useful other than a User-Agent of Mozilla/5.0 (Windows NT 6.1;
WOW64; rv:37.0) Gecko/20100101 Firefox/37.0

Because it was an EC2 instance that was at the originating IP address, I also collected my notes on the behavior and reported it to Amazon Web Services: AWS: Report Abuse.

On 21 February 2018 I received replies from Amazon. Not particularly satisfying but it’s my hope they’ll continue to take security seriously.


Thank you for submitting your abuse report. We have begun our investigation into the source of the activity or content you reported.

We’ve determined that an Amazon EC2 instance was running at the IP address you provided in your abuse report. We have reached out to our customer to determine the nature and cause of this activity or content in your report.

We will investigate your complaint to determine what additional actions, if any, need to be taken in this case. Due to our privacy and security policies, we cannot provide details regarding the resolution of this case, or the identity of our customer. We may notify you during our investigation if our customer requires more information from you to complete their troubleshooting of the issue. Our customer may reply stating that the activity or content is expected and instructions on how to prevent the activity or manually remove the content, as well. If you wish to provide additional information to us or our customer regarding this case, please reply to this email.

Please note that if we determine the activity or content to not be abusive, we will notify you and resolve the case; we may refrain from communicating further, in that case.

We will notify you once this case has been marked resolved. Thank you for alerting us to this issue.

AWS Abuse Team

And also this, which resolves the closes the case out:


This is a follow up regarding the abusive content or activity report that you submitted to AWS. We have investigated this report, and have taken steps to mitigate the reported abusive content or activity. Due to our privacy and security policies we are unable to provide details regarding the resolution of this case or the identity of our customer.

We are committed to mediating reports of abusive content or activity to the satisfaction of both the reporters and our customers. If you believe the reported content or activity persists, or are not satisfied with the resolution of this case, please reply directly to this message with more information. Your response should include the most recent activity logs or web location of the content that you have available that indicates that the activity or content persists, as well as a clear, succinct explanation of what you expect of us and our customer.

Thank you for bringing this matter to our attention.

AWS Abuse Team

I hope this proves useful to folks. If you’re running self-hosted WordPress, do take a serious look at WordFence.

Swarm Checkins

Blog Post Visualization + Code

I’ve been doing maintenance galore on this site. I have a hand-written theme and I’ve been doing things like returning the footer to a good shape and sweating the responsive details of it.

I first wrote that code in 2010 but I’ve adapted it to be a bit better and now am using CSS Grid to allow those columns to display a bit better on different screen sizes and form factors.

The header has a similar but not quite the same treatment on archive pages to allow navigating from month to month and year to year quickly.

Like I said, I first wrote the code for the years and months visualization back in 2010.

The PHP code has not changed a great deal from 2010, and it’s available on GitHub. It also includes the SASS source code for the stylesheets. There are additional notes as well.

If anyone has any questions or comments, please give me a shout!

Swarm Checkins

←January 2018March 2018→