Slack is optimized for Firefox version 520

15 Mar 2021

Last week my pal Karl sent me a link to web-bug 67866: which has the cool title “menu buttons don’t work in Firefox version 100”. It turns out that Mozilla’s Chris Peterson has been surfing the web with a spoofed UA string reporting version 100 to see what happens (because he knows the web can be a hot mess, and that history is bound to repeat itself).

The best part of the report, IMHO, is Chris’ comment:

I discovered Slack’s message popup menu’s buttons (such as “Add reaction” or “Reply in thread”) stop working for Firefox versions >= 100 and <= 519. They mysteriously start working again for versions >= 520.

(I like to imagine he manually bisected Firefox version numbers from 88 to 1000, because it feels like something wacky that I would try.)


<aside> + spoiler The bug described below is a slightly different class of bugs than the typical “regexp expected a fixed set of integers and fell on its face, which is where the famous Bill Gates quote “9 major browser versions ought to be enough for anybody” came from (see also Opera version 10 drama, and macOS version 11 drama, etc). And still a different class of bugs from the even more fun and possibly not true explanation for Windows 10 coming after Windows 8 (because software was sniffing for strings that started with “Windows 9”) and going down paths assuming Windows 95 or Windows 98).

picture of bill gates saying 9 browser version should be enough for anybody

</aside>


Broken website diagnosis wizard Tom Wisniewski followed a hunch that Slack was doing string comparison on version numbers, and found the following code:

  const _ = c.a.firefox && c.a.version < '52' ||
            c.a.safari && c.a.version < '11'
              ? h
              : 'button',

(We’re just going to ignore what h is, and the difference between that and button presumably solving some cross-browser interop problem; I trust it’s very clever.)

So this is totally valid JS, but someone forgot that comparison operators for strings in JS do alphanumeric comparison, comparing each character between the two strings (we’ve all been there).

So that’s how you get the following comparisons that totally work, until they totally don’t:

> "10" < "1"
false // ok

> "10" < "20"
true // sure

> "10" < "2"
true // lol, sure why not

So, how should you really be comparing stringy version numbers? Look, I don’t know, this isn’t leetcode. But maybe “search up” (as the kids say) String.prototype.localeCompare or parseInt and run with that (or don’t, I’m not in charge of you).