Where do OSS projects go when they die?

Issue #302.July 1, 2024.2 Minute read.
Bytes

Today’s issue: Expo’s blessing, a new way to make beautiful toast(s), and an interactive study of queueing that every Londoner can be proud of.

Welcome to #302.


Eyeballs logo

The Main Thing

A bunch of people looking concerned

When you see a script for googie-anaiytics embedded in your HTML

Where do OSS projects go when they die?

Reading about an old OSS library being transformed into malware feels like watching one of those YouTube videos about how hot dogs are made — it’s highly upsetting, but also, what did I expect?

The latest victim is Polyfill.js, a library that enables sites with modern JavaScript features to work in older browsers. After evergreen browsers made the project obsolete, it was on the path to dying a slow, unremarkable internet death. But as Sansec Research recently discovered, the project became the subject of a massive supply side attack.

Here’s how it happened: The library was distributed by a CDN called polyfill.io, which reads the User-Agent of a request and returns the polyfills needed by that particular browser. Back in February, a Chinese company purchased the GitHub repo and domain for the CDN and started injecting a fake analytics script for www.googie-anaiytics.com onto the 100,000+ sites using the service.

Ok, but why did they do this? And why does this keep happening?

Money, of course. We’ve previously written about how sketchy websites, hawking everything from CBD gummies to wiener pills herbal supplements for men, will sponsor open source projects to get a backlink from the project’s sponsor page. This supply side attack feels like the next evolution of that strategy.

Instead of paying a monthly fee to get one good backlink, shady companies can buy a project like Polyfill.js and use it to inject links to their sports betting website on thousands of pages. As an added bonus, this particular attack also secretly redirected mobile users of the affected websites to the company’s betting site at random. Delightful.

Bottom Line: Fortunately, once it was discovered, the creator of polyfill.js (not the owner of the domain) told everyone to stop using the project, and Cloudflare and Fastly published safe versions of the service for users to migrate to. But as long as sketchy companies remain some of the most willing backers of OSS, there’s a chance we could be playing Whack-a-Mole with issues like these in the future.

        

bright data logo

Our Friends
(With Benefits)

A cat crying outside the door

When your web scraper gets blocked

Build unstoppable web scrapers with Bright Data

They give you all-in-one scraping infrastructure with unlimited scalability – so you can always get the data you need, without getting blocked or rate limited.

Here’s how:

  • Run your Puppeteer, Selenium, and Playwright scripts on fully hosted browsers (this Fireship video gives a great demo)

  • Overcome blocks with their CAPTCHA auto-solvers, automated proxy rotation, and 72 million residential IPs

  • Easily manage all your scrapers from a single API and save money with their auto-scaling infrastructure that (92% of dev teams report reduced operational costs)

Try the scraping browser API for free – and see why orgs like Stanford and Microsoft use Bright Data to get the data they need.


Spot the Bug logo

Spot the Bug

Sponsored by Apryse

Their document automation tool can integrate with any data source to help you save a ton of time when generating internal reports.

function reduceLanguages (str, lang, i) {
  if (i === this.languages.length - 1) {
    return `${str} and ${lang}.`;
  }

  return `${str} ${lang},`;
}

const user = {
  name: "Tyler",
  age: 27,
  languages: ["JavaScript", "Ruby", "Python"],
  greet() {
    const hello = `Hello, my name is ${this.name} and I know`;

    const langs = this.languages.reduce(reduceLanguages, "");

    return hello + langs;
  },
};

const greeting = user.greet()

Cool Bits logo

Cool Bits

  1. Sam Rose wrote this Interactive study of queueing strategies, and I can already hear our British readers getting excited.

  2. The React Native Team has officially declared that frameworks like Expo are now the recommended approach to create new React Native apps. They are also upgrading Evan Bacon to the rank of Advanced Mage and are awarding him 40 extra mana per turn.

  3. New Relic just teamed up with NVIDIA to create a new AI Monitoring platform that provides a streamlined path for developing, deploying, and monitoring AI-powered enterprise applications in production. It’s a game-changer for anyone building enterprise AI apps. [sponsored]

  4. Marc Andreesen gave a two-hour podcast interview about The origin of Mosaic and Netscape, and he didn’t even talk about San Francisco politics once 👏.

  5. Mary Branscombe wrote about How JavaScript is finally improving the module experience.

  6. Michael Thomas and Thomas Steiner wrote about Why Google Sheets ported its calculation worker from JavaScript to WasmGC.

  7. CarbonQA gives your dev team high-quality QA services that scale. Their US-based testers work directly with your tools and break your app repeatedly, so you never have to waste eng time on QA testing again. [sponsored]

  8. Kath and Kelly wrote about How they built a giant, JavaScript-powered flipdisc display.

  9. The Enhance team recently released a TypeScript Starter for their HTML-first full-stack web framework.

  10. Solid Toast is a new Solid.js library for creating “beautiful, customizable toasts” – which is the exact pitch I gave General Mills for my Cinnamon Toast Crunch fast-casual restaurant chain.


Spot the Bug logo

Spot the Bug: Solution

Sponsored by Apryse

If you run the original code, you’ll get “Uncaught TypeError: Cannot read properties of undefined (reading ‘length’)“. The only place we’re using .length is inside our reduceLanguages function, so we can infer that this.languages is undefined and we’re incorrectly using the this keyword.

Whenever you’re trying to figure out what the this keyword is referencing, you need to look at where the function using the this keyword is being invoked. In our example, we don’t know since it’s in the implementation of reduce. To make sure reduceLanguages gets invoked in the right context and therefor has the correct this binding, we have two choices. First, the old school way - using .bind.

greet() {
  const hello = `Hello, my name is ${this.name} and I know`;

  const langs = this.languages.reduce(reduceLanguages.bind(this), "");

  return hello + langs;
}

Second, the better way - using an arrow function.

greet() {
  const hello = `Hello, my name is ${this.name} and I know`;

  const langs = this.languages.reduce((str, lang, i) => {
    if (i === this.languages.length - 1) {
      return `${str} and ${lang}.`;
    }

    return `${str} ${lang},`;   
  }, "");

  return hello + langs;
}

The reason this works is because with arrow functions, this is determined lexically. Arrow functions don’t have their own this. Instead, just like with variable lookups, the JavaScript interpreter will look to the enclosing (parent) scope to determine what this should reference.

Want to read more? Check out Understanding the “this” keyword, call, apply, and bind in JavaScript.