Stories from the Field, #2: The customer is not always right

(note: all “Stories from the Field” are true, thinly anonymized to protect the -usually- guilty)

Project Manager: Hi Jim and team, we need a new version of our product with this and that new feature.

Team: Sure, but it’ll need a bit more memory on our customers’ computers.

PM: No worries, our customers have more than adequate computers already.

Team: Hmmmokey

Team: (codes)

Team: (tests)

Team: It’s ready and has passed our internals tests successfully.

Team: Now it’s time to go and test it on a few customers just to be safe, like we did last time.

PM: Yeah about that.

Team: WHAT NOW THIS IS A GOOD THING

PM: No no no don’t get me wrong, our customers loved it.

PM: In fact they loved it so much that they have been asking for it.

PM: Jim can you talk to Sales? They’ll tell you some great customers to test with.

Jim: (makes the rookie mistake and calls Sales)

Sales: Oh hi so happy to hear you.

Jim: Really? You usually complain about how our product is a piece of shit.

Sales: AAAAHAHAHAFUNNY no really I’m totally happy to hear you.

Sales: I’ve got a totally great customer you can test the new version with.

Sales: Who is totally not a cheapskate bloodsucking asshole.

Sales: You should totally call him.

Jim: I have a bad feeling about this.

Jim: (calls customer)

Totally Not A Cheapskate Customer: Oh hi happy to hear you, you’re supposed to give me free product to resell to my customers right?

Jim: NO NOT AT ALL we need to test our product and therefore 1) yes you get something for free but 2) be aware that your computers need to have more memory and 3) it’s still being tested which means DO NOT CHARGE YOUR CUSTOMERS FOR IT AND PLEASE TELL THEM THIS MIGHT NOT WORK.

Totally Not A Cheapskate Customer: Yada yada yada so I get free stuff great see you on Thursday bye.

Jim: (has a very bad feeling about this)

Jim and a Teammate: (show up early)

Jim and a Teammate: (start installing the new version on the Totally Not a Cheapskate Customer’s computers)

Jim and a Teammate: Wait this doesn’t work you don’t have enough memory here.

Jim and a Teammate: This isn’t even enough memory for the previous version.

Totally Not A Cheapskate Customer: SO WHAT I DON’T CARE ARE YOU TRYING TO STEAL THE FOOD OF MY CHILDREN? (* actual quote)

Jim and a Teammate: No wait we made very very clear that…

Totally Not A Cheapskate Customer: YOU ARE WORSE THAN THIEVES (* actual quote)

Totally Not A Cheapskate Customer’s Customers: WHY DON’T YOU GIVE US CHEAP STUFF AREN’T WE CITIZENS OF THIS COUNTRY??? (* actual quote)

Totally Not A Cheapskate Customer: I’LL CALL YOUR BOSS RIGHT NOW

PM: Hi Jim what’s going on there?

Jim: This and that, the Totally Not A Cheapskate Customer turned out to be a Totally Cheapskate Customer.

PM: Hmmm so Sales wasn’t 100% sincere.

PM: Who would’ve thought.

PM: Can you remove the installation restriction just for now?

Jim: I can but some things might work, some might not.

PM: Do your best.

Team: (does their best and has a special version ready within 15 minutes)

Jim and a Teammate: (install the software)

Jim and a Teammate: See it kind of works but it has issues BECAUSE IT NEEDS MORE MEMORY.

Totally Not A Cheapskate Customer: SEE MY LOYAL CUSTOMERS I HAVE SLAIN THE THIEVING EVIL CORPORATE DRAGON AND GAVE YOU CHEAP STUFF.

Jim and a Teammate: Wait you actually charged them for this we specifically asked you not to.

Totally Not A Cheapskate Customer: BEGONE YOU FOUL DEMON.

Jim: (silently curses in languages he doesn’t even speak)

Git: how to avoid checking in secrets (using a Powershell pre-commit hook)

Who among us hasn’t found him- or herself in this very awkward position: committing a config or code file with secrets (such as passwords or API keys) and then semi-panicked googling how to delete it from source control.

Been there and let me tell you the easiest way to delete it: copy all the code on disk, delete the repository completely and then re-create it.

(if this is not an option, well, there’s still a way but with much more work and risk, so do keep that code backup around!)

But you know what’s even better? That’s right, avoid this in the first place! That’s why Git hooks are so useful: they work without you neededing to remember to check your config files every time.

So here’s my solution to this:

  1. In the repository, go to .git/hooks and rename pre-commit.sample to pre-commit (i.e. remove the extension)
  2. Open pre-commit with a text editor and replace its contents with the following:
#!/bin/sh
C:/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -ExecutionPolicy Bypass -Command '.\hooks\pre-commit.ps1'
  1. Add a new directory on the root of the repository named hooks.
  2. Inside this, add a text file named pre-commit.ps1 with the following code:
#
# Source: DotJim blog (http://dandraka.com)
# Jim Andrakakis, July 2022
#
Clear-Host
$ErrorActionPreference='Stop'

# ===== Change here =====
$listOfExtensions=@('*.xml','*.config')
$listOfSecretNodes=@('username','password','clientid','secret','connectionstring')
$acceptableString='lalala'
# ===== Change here =====

$codePath = (Get-Item -Path $PSScriptRoot).Parent.Parent.FullName

$errorList=New-Object -TypeName 'System.Collections.ArrayList'

foreach($ext in $listOfExtensions) {
    $list = Get-ChildItem -Path $codePath -Recurse -Filter $ext

    foreach($file in $list) {
        $fileName = $file.FullName
        if ($fileName.Contains('\bin\')) {
            continue
        }
        Write-Host "Checking $fileName for secrets"
        [xml]$xml=[xml]((Get-Content -Path $fileName).ToLowerInvariant())
        foreach($secretName in $listOfSecretNodes) {
            $nodes = $xml.SelectNodes("//*[contains(local-name(), '$secretName')]")
            foreach($node in $nodes) {
                if ($node.InnerText.ToLowerInvariant() -ne $acceptableString) {
                    $str = "[$fileName] $($node.Name) contains text other than '$acceptableString', please replace this with $acceptableString before commiting."
                    $errorList.Add($str) | Out-Null
                    Write-Warning $str
                }
            }
        }
    }
}

if ($errorList.Count -gt 0) {
    Write-Error 'Commit cancelled, please correct before commiting.'
}

So there you have it. I’m getting automatically stopped every time I tried to commit any .xml or .config file that contains a node with a name that contains username, password, clientid, secret or connectionstring, whenever the value of it is not ‘lalala’.

Obviously the extensions, node names and acceptable string can be changed at the top of the script. You can also change this quite easily to check JSON files as well.

Also note that this works on Windows (because of the Powershell path in the pre-commit hook) but with a minor change in the pre-commit bash script, you should be able to make it work cross-platform with Powershell core. I haven’t tested it but it should be:

#!/usr/bin/env pwsh -File '.\hooks\pre-commit.ps1'

Have fun coding!

Signs that you need coffee, #7

You do the first coffee break of the day, say around 10:00.

You sit at the table, feeling sleepy, wondering why the coffee hasn’t had any effect on you.

After ten whole minutes you realize you didn’t actually prepare the coffee.

I’m sure there’s some joke about vicious circles here, but I haven’t had my coffee so I’m too sleepy to think what that might be.