Half-Elf on Tech

Thoughts From a Professional Lesbian

Author: Ipstenu (Mika Epstein)

  • Plugins: Finally, a Decent Bribe!

    Plugins: Finally, a Decent Bribe!

    There was a problem company.

    Among other infractions, they’d sockpuppeted a few years back but calmed down. Or so we thought. Then the forums team pinged Plugins with a list of 415 separate user accounts. You read that correctly. But it was worse than it seemed. Using l33t DNS skills, we traced the sock puppets to not one, not two, but three separate accounts!

    Three. Separate. Plugin developer. Accounts. No, it’s not a return of Zorro!

    Let’s call them Shemp, Moe, and Curly!

    The Three Stooges

    When I got reports about fake accounts, I always pull up the plugins to see if I’d left notes. One of the things the notes includes is adding and removing people with commit or support access, and via that I was able to see they’d been trading plugins between the three accounts. The same 6 or 7 (I forget the exact number) plugins would add one of the other people to the plugin, remove themselves, and then repeat it a few months later.

    People like this actually why we now track when people are added to plugins and when the ownership is transferred. It’s generally a bad-faith move to swap around that much, and honestly I’ve never understood why people would bother.

    So I did what I do and checked the submission IPs. Lo, Shemp had used multiple IPs to submit their plugins, and two of those were used by Moe and Curly. That of course isn’t rock solid proof, so I sent three slightly different emails to each one. 

    • Moe had their plugin closed and was told that for the sin of egregious sockpuppeting (most of the fake reviews had to do with their plugins) they were banned. Also since Moe had been warned before, they weren’t getting back in. This was strike three for the same offence.
    • Curly was told that their plugins were closed because account was connected back to another which had seriously violated the guidelines, and could they please explain the connection so their plugin could possibly be restored.
    • Shemp was told that DUDE WHAT THE HELL? Banned for super spamming, making multiple accounts, etc etc. I told him there were over 500 accounts, though.

    Only Shemp replied.

    Confessions of a Stooge

    Shemp’s reply was …. interesting. First he argued they’d had no violations for a month (which is confirming the previous issues), he then admitted that Moe was indeed his other account, and asked for another chance. After all, he had hired a marketing company and it was all their fault. Oh and by the way, Shemp was partners with Curly.

    So I pointed out Curly had been emailed separately and if they were indeed a separate company, Curly should reply to that email. (Narrator: they did not and have not). Then I explained that when you hire someone to do a job and they fuck up, it’s your responsibility. Especially when you hire a company that does that level of spamming after you’d been warned specifically about it three separate times. 

    Shemp replied, saying he understood how serious the matter was (doubtful) and was accepting responsibility. Please have mercy for people who did honest work. Except the part about the sockpuppeting and multiple accounts. Please give Shemp and his company a second chance.

    Technically it was their fourth. And frankly after you’ve been warned once to not make fake accounts, a business should have the sense to stop. Or, as I said:

    Making the same mistake three more times after you were warned means you either didn’t care enough to monitor your consultants or you didn’t think we were serious when we told you the last time that repeat offenses will result in being banned.

    Me to Shemp via email

    This was followed with a reminder of how one can host code outside of wp.org, have a nice day.

    Time for Eye Poking

    Shemp said he was shocked to know it was this bad. Shemp had also gotten and opened (and I presume read) an email with the subject FINAL WARNING RE BEHAVIOUR ON WORDPRESS.ORG. In all caps. It detailed out everything with fake accounts. Shemp was stunned this ended with the ban I said it would. I know he opened it because we track that.

    This means that Shemp was warned about sock puppets three times, but he continued to hire the companies that made the fake accounts, and he was surprised there are consequences? The ‘reason’ for the mistake is probably that the separate teams that used the separate accounts were unmonitored and unmanaged, allowing them to do whatever they wanted. And see, this is why multiple accounts like that is stupid.

    Then came the best bribe I ever got!

    I have an offer for you. 50% of revenue lifetime, not the income but revenue for you!

    Shemp via email to Plugins

    Admittedly, his revenue was probably pretty slim, but it was way better than the $10 and $50 bribes I usually got. Oh and Shemp promised to never tell anyone I accepted a bribe.

    How does that saying go? Two can keep a secret if one is dead?

    Declined. Banned harder. Curly and Moe (being probably all Shemp) never replied.

    PS…

    A week later, Shemp slacked me and offered to ‘fund’ LezWatch.TV for me. Which led to him getting banned on Slack when I mentioned it to the admins.

    Later I told my forum sock-puppet-hunter friend about this, and he asked why I told them fake numbers for the amount of sock puppets. I like to give them wrong numbers on purpose. Once I told someone he had 20 sock puppets and he argued he only made 10.

    It’s like working with toddlers some times.

  • Looping LinksWith The WP HTML Processor

    Looping LinksWith The WP HTML Processor

    Here’s your backstory.

    You need to search all the links in a post and, if the link is to a specific site (wikipedia.com) you want to add it to an array you output at the bottom of your post, as citations. To do this, you will:

    1. Search for tags for every single link (<a href=.... )
    2. If the link contains our term (Wikipedia), put it in the array
    3. If the link also has a title, we’ll use that

    If we do all that, our output looks something like this:

    • Source: https://en.wikipedia.com/wiki/foobar
    • Source: Foobar2

    While you can do this with regex, you can also use the (new) HTML Processor class to do it for you.

    RegEx

    As I mentioned, you can do this with regex (I’ll spare you the drama of coming up with this in the first place):

    $citations = array();
    
    preg_match_all( '#<\s*a[^>]*href="([^"]+)"[^>]*>.*?<\s*/\s*a>#', get_the_content(), $matches );
    
    // Loop through the matches:
    foreach ( $matches[0] as $i => $match ) {
    
        // If the URL contains WikiPedia, we'll process:
        if ( str_contains( $match, 'wikipedia.com') ) {
    
            // Build initial data:
            $current_citation =[
                'url'   => $matches[1][$i],
                'title' => $matches[1][$i],
            ];
    
            // If there's a title, use it.
            if ( str_contains( $match, 'title=' ) ) {
                  $title_match = preg_match( '#<\s*a[^>]*title="([^"]+)"*[^>]*>.*?<\s*/\s*a>#', $match, $title_matches );
                  $current_citation['title'] = ( ! empty( $title_matches[1] ) ) ? $title_matches[1] : $current_citation['title'];
            }
        }
    
         $citations[] = $current_citation;
    }
    
    ?>
    <ol>
        <?php foreach ( $citations as $citation ): ?>
            <li itemprop="citation" itemscope itemtype="https://schema.org/CreativeWork">
                 Source: <a rel="noopener noreferrer external" itemprop="url" class="wikipedia-article-citations__url" target="_blank" href="<?php echo esc_url( $citation['url'] ) ?>"><?php echo esc_html( $citation['title'] ) ?></a>
             </li>
         <?php endforeach; ?>
    </ol>
    

    This is a very over simplified version, but the basis is sound. This will loop through the whole post, find everything with a URL, check if the URL includes wikipedia.com and output a link to it. If the editor added in a link title, it will use that, and if not, it falls back to the URL itself.

    But … a lot of people will tell you Regex is super powerful and a pain in the ass (it is). And WordPress now has a better way to do this, that’s both more readable and extendable.

    HTML Tag Processor

    Let’s try this again.

    What even is this processor? Well it’s basically building out something similar to DOM Nodes of all your HTMLin a WordPress post and letting us edit them. They’re not really DOM nodes, though, they’re a weird little subset, but if you think of each HTML tag as a ‘node’ it may help.

    To start using it, we’re going to ditch regex entirely, but we still want to process our tags from the whole content, so we’ll ask WordPress to use the new class to build our our tags:

    $content_tags = new WP_HTML_Tag_Processor( get_the_content() );

    This makes the object which also lets us use all the children functions. In this case, we know we want URLs so we can use next_tag() to get things:

    $content_tags->next_tag( 'a' );

    This finds the next tag matching our query of a which is for links. If we were only getting the first item, that would be enough. But we know we have multiple links in posts, so we’re going to need to loop. The good news here is that next_tag() in and of itself can keep running!

    while( $content_tags->next_tag( 'a' ) ) {
        // Do checks here
    }
    

    That code will actually run through every single link in the post content. Inside the loop, we can check if the URL matches using get_attribute():

    if ( str_contains( $content_tags->get_attribute( 'href' ), 'wikipedia.com' ) ) {
        // Do stuff here
    }
    

    Since the default of get_attribute() is null if it doesn’t exist, this is a safe check, and it means we can reuse it to get the title:

    if ( ! is_null( $content_tags->get_attribute( 'title' ) ) ) {
        // Change title here
    }
    

    And if we apply all this to our original code, it now looks very different:

    Example:

    		// Array of citations:
    		$citations = array();
    
    		// Process the content:
    		$content_tags = new WP_HTML_Tag_Processor( get_the_content() );
    
    		// Search all tags for links (a)
    		while( $content_tags->next_tag( 'a' ) ) {
    			// If the href contains wikipedia, build our array:
    			if ( str_contains( $content_tags->get_attribute( 'href' ), 'wikipedia.com' ) ) {
    				$current_citation = [
    					'url'   => $content_tags->get_attribute( 'href' ),
    					'title' => $content_tags->get_attribute( 'href' ),
    				];
    
    				// If title is defined, replace that in our array:
    				if ( ! is_null( $content_tags->get_attribute( 'title' ) ) ) {
    					$current_citation['title'] = $content_tags->get_attribute( 'title' );
    				}
    
    				// Add this citation to the main array:
    				$citations[] = $current_citation;
    			}
    		}
    
    		// If there are citations, output:
    		if ( ! empty( $citations ) ) :
    			// Output goes here.
    		endif;
    

    Caveats

    Since we’re only searching for links, this is pretty easy. There’s a decent example on looking for multiple items (say, by class and span) but if you read it, you realize pretty quickly that you have to be doing the exact same thing.

    If you wanted to do multiple loops though, looking for all the links but also all span classes with the class ‘wikipedia’ you’d probably start like this:

    while ( $content_tags->next_tag( 'a' ) ) {
        // Process here
    }
    
    while ( $content_tags->next_tag( 'span' ) ) {
        // Process here
    }
    

    The problem is that you would only end up looking for any spans that happened after the last link! You could go a more complex search and if check, but they’re all risky as you might miss something. To work around this, you’ll use set_bookmark() to set a bookmark to loop back to:

    $content_tags = new WP_HTML_Tag_Processor( get_the_content() );
    $content_tags->next_tag();
    
    // Set a bookmark:
    $content_tags->set_bookmark( 'start' ); 
    
    while ( $content_tags-> next_tag( 'a' ) ) {
        // Process links here.
    }
    
    // Go back to the beginning:
    $content_tags->seek( 'start' ); 
    
    while ( $content_tags->next_tag( 'span' ) ) {
        // Process span here.
    }
    
    

    I admit, I’m not a super fan of that solution, but by gum, it sure works!

  • Plugins: What a B*tch

    Plugins: What a B*tch

    The old tagline on this site was “Half Elf, Full B.I.T.C.H.”

    I removed it ages ago because, while I got the joke, it didn’t always translate well. The tagline was actually from Tabatha Coffey, who was a contestant on a Bravo reality show Sheer Genius, trying to find the best haircutter. The show was awful, Tabatha was not. She had her own show for a while, Tabatha’s Salon Takover, and she was basically the Gordon Ramsey of haircuts.

    At some point, she ‘took back’ the word “bitch” and said it stood for Brave, Intelligent, Tenacious, Creative, and Honest.

    I like to think I’m that.

    Anyway. This is short and our subject won’t even get a name. That’s how annoying he is.

    User submits a big plugin. On review, which doesn’t take long, it’s found to be a rebranded copy Elementor. It wasn’t even a well done copy, it barely changed name and there was no new code. The plugin was rejected with the normal note of “hey, this looks like you meant to upload a plugin to your site. We’ve rejected it. Please don’t do that.”

    The email is actually pretty kind and explains what the plugin submission form was for and all, and how to properly upload to your own site. While it’s weird to me, that issue happens all the time, and it’s part of why we have code that checks the name and auto-prevents you from uploading things with the same name (even if it has a different slug).

    The reply?

    you bitch.

    I thought that was a little extreme and suspended the account, emailing the standard boilerplate to explain why (tl;dr – jumping to that is just not something a mature human should do).

    Our edge lord du jour snaps back:

    you call that extreme? you asked for it because you’ve rejected my plugin!

    Okay, kid. Have a nice life.

    He was never heard from again.

  • Plugins: Sex and Gutenberg

    Plugins: Sex and Gutenberg

    Look, I get that a mess of people hate Gutenberg. I like it, but that’s my opinion and that’s okay. I’m fine if you hate something I like, it doesn’t hurt me at all. What I don’t like are people being jerks to Gutenberg devs. In fact, I truly dislike people who are just mean to anyone, Dev or not. One of my weirder jobs as the Plugin Rep was balancing protecting users and protecting developers.

    It gets really weird when the developer is the user.

    I Hate Gutenberg

    That’s how we’ll start this one. Alan (not his real name) hated Gutenberg and posted a ‘review’ that was basically offering to bribe Automattic to stop making it. The forum mods very nicely said “hey man, this isn’t a real review, could you do that instead?”

    Alan…

    I was under the impression that ultimately Automattic is in control of the WordPress Core. If not them, then someone is, the buck stops somewhere. The drive to make this core is most likely coming from wordpress.com, an Automattic asset that uses themes that do not include a page builder.

    Alan on the WordPress.org Forums (post redacted)

    He went on to try and argue about usability etc. Now, to his credit, this was in the early days when it absolutely had some accessibility issues. It was also at the era that it was en vogue to bash Gutenberg … I’m not sure that era has ended yet. But basically Alan, no. WordPress.org is not going to stop developing Gutenberg. And no, Automattic doesn’t rule the world.

    The thing that was a little interesting is that Alan had left a few other turds of reviews (the eloquent “Don’t waste your time.” and using reviews for support). Five months prior, he’d got shirty about a review being moderated and not approved in a ‘timely’ (i.e. 5 minutes) fashion. Seriously. Five goddamn minutes.

    Kickbacks?

    The weekend rolls around. Most people are off enjoying the world and not WordPressing. That’s when Alan uses his second account to leave another review. His second account was, interestingly, the one he used to own his plugins. You can see how this landed in my lap.

    His other account was the one I was familiar with since he’d had a weird complaint that WooCommerce wasn’t accepting new plugins on their site a couple years ago, and he tried to buy someone’s plugin so he could be there. Alan also left a bunch of crappy reviews as that account, including a rant that someone charged people for their service (admittedly that plugin did do an asshat move by switching from free to pay, but still, the review was “They charge!” and not “The plugin sucks because …!” which is different).

    So on the weekend, Alan runs into another post by someone who mentions a plugin they’re using is conflicting with something. This was not Alan’s plugin. But Alan decided he’d fix it. He replied three times within 30 minutes that he was going to fix it, had ideas about fixing it, and had a fix. The robots (aka Akismet) flagged him as spam for the rapid posting and content since it had links.

    This happens, but it rarely requires anyone to ask this:

    Can a moderator please approve my posts above? I have posted a fix.

    Yes, I know this plugin is bad for your askimet kickbacks, but we want this, not askimet.

    Alan in the Forums on a Sunday

    Kickbacks? Akismet? I guess he realized he was caught as spam.

    And what does Alan do? He gets his other account, the non-plugin-dev one, and repeats his posts with this added on:

    Wow, speak your mind around here, and get the silent treatment. Core needs a fork soon. Ever since Gutenberg actually. Clearly (sexual?) favors are being exchanged, why else would automattic include changes that almost nobody wants, and actively hates? 

    Alan in the forums on a Sunday via his alt account

    … What?

    You Read That Right

    Yes, Alan said sexual favours were exchanged for … Gutenberg? Which had nothing to do with this plugin nor its conflict. And he wasn’t being given the silent treatment, it was a goddamn weekend, and I’ve spent over a decade telling the volunteers of WordPress to take a fucking weekend off!

    Forums reported him to Plugins and I read it on Monday. In the intervening hours, there were more complaints, from both accounts, and more rants that had nothing to do with the plugin that Alan was fixing. Given that he had already been warned the year before about making everything about his Gutenhate …

    A gif of Jan Brady saying "Marcia, Marcia, Marcia!"

    Alan clearly was incapable of (or unwilling to) restrain himself from ad hominem attacks and making everything about Gutenberg. In fact, if we go to tape, the year prior he claimed someone in Switzerland controlled all emails. The thing was, he just … posted that on .Org.

    Not as a reply, not as a comment about a plugin or theme.

    Not as related to a WordCamp.

    The post was really just “This website [WordPress.org] controls all email and are using their power to destroy my business.”

    It was clear to me what had to happen.

    Besides the fact that you were posting on a weekend and we are a 100% volunteer run service, accusing people of sexual favors in this manner is an egregiously unwelcome way in which to behave in public. It is aggressively offensive, rude, and in violation of multiple forum and plugin guidelines.

    Using a second account with which to make those comments shows you did this absolutely with malicious intent. You didn’t even wait an hour between asking your post to be approved and leaving such a comment. This behavior is an escalation to your aggressive, and incorrect, attack on Automattic regarding Gutenberg last year.

    We feel your actions demonstrate you simply are not willing to be a productive member of this community and as such we are invoking our right to remove hosting of your plugins at any time.

    Me via email to Alan

    There’s a lot more boilerplate, but more or less it tells you “Don’t make another account, and stop wasting everyone’s time here.” If you can’t play well with others, then open source isn’t going to be the place for you.

    In My Defence…

    Alan began his reply by saying he never intended that part of his reply to be public. So he … made it on a public forum, in the hopes his alternate account wasn’t blocked? Even though he knew about Slack and regularly used it to complain about being moderated.

    He went on to complain that the forum mods had a vendetta and clearly knew him from work. I have to admit, I blinked a lot there. His excuse was people clearly hated him from work and it spilled over? Why would they hate you at work, Alan? Why would someone from your job hate you so much as to make up shit about you here? He blamed the PC ‘woke’ world, and demanded to know who said such things because he had never seen any complaints. The same complaints that carried over from his work.

    Then he went on to say it was clearly a vendetta of the plugins team against non-Automattic owned ‘security’ apps. His was used by, I think, 500 or so people. Not what I consider a ‘well used’ plugin, more of a niche thing. This isn’t a judgement call! I have plugins used by fewer than that. It’s just like saying Lindys sells more cheesecake than strudel.

    That was a Guys & Dolls joke.

    But on topic, let’s see. The takeaways from Alan’s email are:

    1. Intent to be abusive? Check.
    2. Not understanding that multiple people are admins and can read posts? Check.
    3. Demanding explanation for non-related topics? Check.
    4. Claims he was never warned even though he replied to those warnings? Check.
    5. Claims we’re sensitive after accusing people of sexual impropriety? Check.
    6. Hiding behind claims of ‘woke’ abuse? Check
    7. Incorrectly assuming it’s about plugin in question? Check.

    Conclusion? Angry person who thinks he can mistreat anyone. And just for a bonus:

    Further, based on this overreaction, I can only conclude that I am correct. Gutenburg’s authors are providing sex, money and drugs to high ranking decision makers at Automattic.

    Alan via email to Plugins

    I still don’t (never have) work for Automattic. I’ve been to a lot of WordCamps and I’ve never been offered money or drugs or sex for favours doing plugin reviews. Was I always talking to the wrong people? Was there a secret orgy I was excluded from!? Now I want to know!

    Not really. None of that has any place in my WordPress life. Sorry folks, my sex life is not related to plugins or WordPress, and very much not your business.

    And for Alan, I just shrugged and moved on.

  • Plugins: My Way Or Nothing

    Plugins: My Way Or Nothing

    Back when I started explaining why I dislike security plugins, I mentioned hating people who thought they knew everything.

    Noah (not his real name) is a good example of that kind of drama.

    Not Safe Enough

    Very rarely do plugins get an email that starts out with claims we’re too dumb to understand security.

    In this case, Noah submitted a search plugin. Not a security plugin. And on a quick look, it had the following serious issues:

    • direct access to files with POST calls outside of functions
    • calling wp-config.php directly to get DB access
    • non-prefixed functions, defines, and classes
    • non-sanitized data being processed and non-escaped data being output

    Pretty normal in so far as poorly written plugins go, and Noah got an email with the usual details on what’s wrong and what to fix.

    Noah did not take it well and claimed our rules were arbitrary and made to protect people. Yes? I mean, of course the rules are there to protect people! And one can argue any rule is ‘arbitrary,’ but that’s how they work. Noah went on to say that was okay, but rules had disadvantages too. To that end, sure, he’s got a point. The rules do limit innovation to an extent but usually that’s got a reason.

    The email went on for 1500 words about the problems people face (resource usage, mostly, and caching), and Noah included this gem:

    You are not able to solve these problems since you waste all of your time defining rules, handling security problems and bothering people who are offering their assistance to you for free.

    Noah via email to Plugins

    We wouldn’t have to make all those guidelines and handle security issues if people actually fixed them, but I felt that was besides the point.

    Let’s be honest here, Noah’s email was a lot of drama and words, signifying nothing at all. Making WordPress faster and having it use fewer resources is a great idea. But sir, this is an Arby’s. Or rather, Noah my man, this is a search plugin that has obvious security issues (sanitizing/escaping), makes dangerous calls that won’t always work (calling wp-config.php which can be moved), and crap for prefixes.

    Not Smart Enough

    I did pick out the line “you can’t understand what I do” and rolled my eyes. Generally speaking, if someone tells you that, they’re the fool, not you. It’s like people who jump to tell you “I have a decade experience in WordPress!” They want their perceived standing in the community to excuse behaviour. That may happen, but I never cared if it was Matt himself submitting a plugin. Security is security. Hell, I once closed my own plugin for a security issue I’d missed 5 years prior (hilariously I only realized it was mine when I was about to hit send — there was a lot of laughing).

    And I absolutely am smart enough to take one look at Noah’s code and recognize that his entire point was to have a plugin search WordPress and intentionally not use the built in WordPress security features. Like nonces. Normally that is from a lack of education and I think of it as a no-harm/no-foul. I remember when I didn’t really understand nonces, after all!

    But in Noah’s case, he believed he was smarter than everyone else using WordPress and, instead of submitting patches to improve it for everyone, he was just going to circumvent WordPress’ security entirely. And that is a non-starter.

    I am also smart enough to see his plugin could be brought into compliance pretty easily, which was why I didn’t reject, I pended.

    Not Clever Enough

    I replied to Noah explaining his rant was pretty off topic not to mention very wrong in many places. The code was not safe to use, and we generally didn’t accept plugins that didn’t ‘use’ WordPress unless it was safe.

    Perhaps I should explain a little here… See, there are two issues with calling wp-load.php or wp-config.php. The first issue is that people can, and do, move both the config file and the wp-content folders, so there would have to be a lot of fallbacks to make sure it would work for everyone. The second is that calling those directly is how you obviate WordPress’ security.

    A sneaky third is that there’s no reason you should need to do that in the first place. If you call your plugin properly, with init and so on, then you get all the fun WordPress stuff without the drama. I know it’s a concept a lot of pure PHP devs struggle with, especially when they want to have ‘a url’ that people can use on their site (like a plugin wants your site to have example.com/myplugin), because that is a little tricky with WordPress.

    In the email to Noah I made it clear. If he wanted to be hosted on WordPress.org, he had to follow the guidelines and that included our security requirements.

    I mean, come on, here’s the part of the email where I tell him to sanitize, and he asks if that’s correct:

    > When you include POST/GET/REQUEST/FILE calls in your plugin, it’s important to sanitize, validate, and escape them.

    Did you ever think, if this is really correct?

    Noah via email to Plugins

    Yes. Yes I do think it’s correct to sanitize, escape, and validate.

    Also telling him “you must use the most appropriate function for sanitizing” (by which I mean ‘don’t sanitize a number as a text field, y’all’) he says:

    Who are you, that you think you are allowed to tell me what I “must use”? Are you thinking, you would be god?

    Noah via email to plugins

    A god?

    A screenshot that says "I am a generous god."

    Jokes aside, Noah made it clear he thought the rules were bullshit, and he had no intention of ‘helping’ anyone. I shrugged, rejected his plugin, suspended his account, sent his emails to the blocked bin, and moved on my way.

    One Year Later…

    A new search plugin showed up, and at first it didn’t trigger any memories for me. In fact, this version had corrected most of the things I’d flagged in the first one. A lot of people submit similar plugins, but at this point I was about 7ish years into reviews, so I was pretty good at spotting repeats.

    Just like people have writing styles, people have coding styles.

    Around halfway through the review, something clicked. I went and checked, and lo and behold it was Noah! He used a new domain, but DNS showed the same person owned both domains. Him. And he wasn’t really trying to hide it since, when I rejected the plugin, he replied from his first account’s email.

    He explained that in the last couple of weeks, we’d ‘twice’ gotten in trouble together. I thought I hadn’t heard from him in nearly a year, and now I was worried he had used multiple accounts. But before I got into that work, I read his email.

    I Didn’t Read His Email

    It’s true. I skimmed his 2500+ word email that was filled with … well … bullshit. He claimed that the use of saved replies meant I was ignorant and only doing that because it was easy… You know what, let me bullet point.

    1. He thinks Automattic owns the plugin review team.
    2. He believes he was 100% right and I was wrong, but he made the security changes anyway.
    3. I was selfish for not listening to him, but he’s not rude for not listening to me.
    4. He doesn’t believe telling me I’m stupid and paranoid was rude.
    5. He expected me to do the emotional labor of telling him how to behave.
    6. He claimed I rejected his plugin without a word (even though he replied to the email where I explained it was for abusive behaviour).
    7. He’s mad I rejected his resubmission, even though the original email said NOT to resubmit or make a new account (… I mean …).

    I did not reply since at the end he’d said this:

    This is my very last attempt to offer my software to you. If you don’t answer this email, I will delete the plug-in. But I believe you would serve WordPress better if you would give the software a chance.

    Noah via email to Plugins

    Alas, he tried twice more that year before (seemingly) giving up.

    Oh, and no, he didn’t have other accounts. He was sniffing some glue to come up with a claim that he and I had been in contact at all after that first rejection.

  • Docked Libraries and DNSMasque

    Docked Libraries and DNSMasque

    I use Docker at work because it’s what we have to use to build sites on specific servers (like WordPress VIP). Honestly, I like it because everything is nicely isolated, but it has been known to have some … let’s call them ‘quirks’ with the newer M1 and M2 chip Macs.

    You know what I have.

    And I had some drama on a Friday afternoon, because why the hell not.

    Drama 1: libc-bin

    After working just fine all day, I quit out of Docker to run something else that likes to use a lot of processing power. When I popped back in and started my container, it blew a gasket on me:

    21.39 Setting up npm (9.2.0~ds1-1) ...
    21.40 Processing triggers for libc-bin (2.36-9+deb12u1) ...
    22.74 npm ERR! code EBADENGINE
    22.74 npm ERR! engine Unsupported engine
    22.74 npm ERR! engine Not compatible with your version of node/npm: npm@10.1.0
    22.74 npm ERR! notsup Not compatible with your version of node/npm: npm@10.1.0
    22.74 npm ERR! notsup Required: {"node":"^18.17.0 || >=20.5.0"}
    22.74 npm ERR! notsup Actual:   {"npm":"9.2.0"}
    

    I was using Node 16 as a holdover from some work I was doing back at DreamHost. Of course the first thing I did was update Node to 18, but no matter what I tried, Docker would not run the right version!

    I looked at the Dockerfile and saw this section:

    # Development tooling dependencies
    RUN apt-get update \
    	&& apt-get install -y --no-install-recommends \
    		bash less default-mysql-client git zip unzip \
    		nodejs npm curl pv \
    		msmtp libz-dev libmemcached-dev \
    	&& npm install --global npm@latest \
    	&& rm -rf /var/lib/apt/lists/*
    

    When I broke it apart, it was clear than apt-get install was installing the wrong version of Node!

    Maddening. I wrestled around, and finally I added FROM node:18 to the top of my Dockerfile to see if that declare would work (after all, Docker supports multiple FROM calls since 2017).

    To my surprise, it did! Almost…

    Drama 2: PHP

    It broke PHP.

    While you can have multiple FROM calls in modern Docker, you have to make sure that you place them properly. Since node was the new thing, I put it as the second FROM call. In doing so, it overrode the PHP call a few lines down, causing the build to fail on PHP.

    Our Dockerfile is using something like the older version of the default file (I know I know, but I can only update a few things at a time, I have 4 tickets out there to modernize things, including PHPCS), I had to move the call for FROM wordpress:php8.1-fpm to right above the line where we call PHP.

    You may not have that. But if you add in that from node and it breaks PHP telling you it can’t run? That’s probably why.

    Huzzah, the build works! PHP and Node are happy … but then …

    Drama 3: UDP Ports

    Guess what happened next?

    Error response from daemon: Ports are not available: exposing port 
    UDP 0.0.0.0:53 -> 0.0.0.0:0: listen udp 0.0.0.0:53: bind: address 
    already in use
    

    I shouted “FOR FUCKS SAKE!”

    I did not want to edit the compose.yml file. Not one bit. If it works for everyone else, it should be that way.

    Thankfully, Docker has a way to override with compose.override.yml (we have a docker-compose.override.yml file, old school name, old school project). I was already using that because, for some dumb reason, the only way to get the database working was to add in this:

    services:
      db:
        platform: linux/amd64
    

    It’s not a super dumb reason, it’s a Docker vs M1 chipset reason. Still, it was annoying as all get out.

    Naturally, I assumed override meant anything I put in there would override the default. So I tossed this in:

      dnsmasq:
        ports:
          - "54:54/udp"
    

    Turns out, override doesn’t mean override when it comes to ports. I went to the documentation, and there is no mention of how to override ports. On to the Googles! A lot of searching finally landed me on an old, closed ticket that implied I could do exactly what I wanted.

    After reading that whole long ass ticket I determined the syntax is this:

      dnsmasq:
        ports: !reset
          - "54:54/udp"
    

    Finally I could get my site up and running! No idea why that isn’t documented, since the dnsmasq issue is a known compat issue with MacOS.

    Drama 4: Update All The Things

    Then I did the dumbest thing on the planet.

    I updated the following:

    • Docker
    • Everything I use from HomeBrew
    • Mac OS (only the minor – I’m not jumping to the next major release on a Friday afternoon!)

    But I have good news this time.

    Everything worked.

    Final Drama…

    The thing was, I didn’t really want to have to edit the Dockerfile. It’s a tetchy beast, and we all use different OSes (I’m M1, one person is older Mac, one is Windows). Cross compatibility is a bit issue. I did test a number of alternatives (like defining engine in package.json and even mimicking our other setups).

    At the end of the day, nothing worked except the above. No matter what, libc-bin was certain I was using NPM 10, which I wasn’t. I wish I’d found a better solution, but so far, this is the only way I could convince my Dockerfile that when you install via apt, you really want to use the latest.