Back to all posts
High CI/CD

The build that should have failed:
a gutted npm package, four years as latest, and the gate that refuses it

On 5 January 2022 the maintainer of faker published version 6.6.6 with no code in it. It is still the latest tag on npm, it still pulls 1.9 million downloads a week, and a plain install of it succeeds. This is why we built DepAlert, and what it does to that build.

CyberXYZ Security TeamThreat Intelligence
7 min read
If a pipeline resolves faker without a pin, it installs 6.6.6

npm install faker resolves to 6.6.6 today. The tarball contains a README and a package.json and nothing else: no index.js, so require('faker') throws. GitHub rates it High under GHSA-5w9c-rv96-fr7g. The last working release is 5.5.3; the maintained successor is @faker-js/faker.

Summary

What happened

faker was one of the most depended-on packages on npm, a generator of fake names, addresses and text used in test suites everywhere. In November 2020 its maintainer wrote that he would no longer support large companies with his free work. On 5 January 2022 he published faker 6.6.6. The package.json still points at index.js; the file is not in the tarball. The README consists of one line, a question about Aaron Swartz. A day later the same maintainer pushed a colors.js release that printed "LIBERTY" in an infinite loop, which is the incident most people remember. Those were two different sabotages: colors was made noisy, faker was made empty.

GitHub suspended the account. npm reverted colors to a safe version. faker was handled differently: the community forked the working code to @faker-js/faker, and the original package was left as its author published it. That is the state of the registry today. The latest dist-tag is 6.6.6, the version is not deprecated, and in the week ending 6 September 2026 the package was downloaded 1,894,806 times.

Nothing in the toolchain says no. The registry serves it, the install succeeds, and the build breaks somewhere else, later.

Why scanners did not stop it, and mostly still do not

No advisory for 76 days, and an advisory is not a block

GHSA-5w9c-rv96-fr7g was published on 22 March 2022, eleven weeks after the release. Any scanner that keys on advisories was blind for that whole window. Since then it is a High-severity advisory, and most pipelines are configured to warn on High, not fail. The version is still served as latest, so every unpinned resolution walks straight into it.

This is the shape of every supply-chain incident, not just this one. The hijacked axios release earlier this year, the event-stream backdoor, ua-parser-js, coa, rc: in every case the malicious version was live and installable for hours or days before an advisory existed. Vulnerability databases are a record of what has been written up. A CI pipeline needs a decision about what it is about to install right now.

Timeline

Indicators

TypeIndicatorContext
Packagefaker@6.6.6Sabotage release, no functional code, current npm latest
Tarball SHA-256719c492d6a4b3642dbef74fa10d17fc44194ace0ea4cf029d5a5041614f4ffaefaker-6.6.6.tgz as served by registry.npmjs.org, September 2026
AdvisoryGHSA-5w9c-rv96-fr7gRemoval of functional code in faker.js, High, published 2022-03-22
Safe versionfaker@5.5.3Last release before the sabotage, 8 April 2021
Replacement@faker-js/fakerCommunity fork of the working code, actively maintained

What DepAlert does with that build

DepAlert is the CyberXYZ dependency gate for CI. It runs before the pipeline installs anything, reads the manifests in the repository, sends the resolved package list to the same decision engine that answers our install-time proxy, and turns the build red if any package comes back malicious or quarantined. It does not wait for a CVE. It fails closed: if the engine cannot be reached, the job fails with its own exit code rather than letting an unchecked package through.

1
Checkout

The job checks out the branch exactly as any other step would.

2
Read the lockfile

package-lock.json, requirements files, Pipfile.lock, poetry.lock and go.sum are parsed into a list of exact versions. Nothing is installed.

3
One batch verdict

The list goes to the engine in a single call. Each version comes back allow, alert, quarantine or block, with the signals that fired and the last safe version.

4
Exit code

0 clean, 1 block, 2 quarantine, 3 alert, 4 engine unreachable. The threshold that fails the build is yours: block by default, quarantine or alert if you want the day-zero signals to gate too.

5
Red build, with the reason

The verdict is written to the job summary, exported for later steps, and the pull request shows the failed check with the package, the scores and the safe version.

This is the real output from the pull request we keep open on the action's own repository. It bumps an example app to faker 6.6.6 and the gate has refused it on every run.

dependency gate (examples/webapp)failed in 15s · pull request #1 · chore(deps): bump faker to 6.6.6
CyberXYZ DepAlert  verdict block (exit 1), see the scan output above
▸ Run CyberXYZSecurity/depalert-action@v1
  xyz depalert scan package-lock.json
  XYZ DepAlert: scanning 2 packages

                        1 Threat(s) Detected
  Package@Version    Decision   Scores       Signals   Safe Version
  faker@6.6.6        BLOCK      CVSS 7.5     2         5.5.3
                                XYZ 3.7
                                EPSS 0.59%

  1 clean  1 blocked  0 quarantined  0 alerted
  Gate FAILED: 1 threat(s) detected (worst: BLOCK)

fig. 01 · the failed check on the live example · express 4.19.2 in the same lockfile passed

Three ways to add it

One step on GitHub Actions, from the Marketplace:

.github/workflows/xyz-depalert.ymlname: CyberXYZ gate on: [push, pull_request] jobs: depalert: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: CyberXYZSecurity/depalert-action@v1 with: api-key: ${{ secrets.XYZ_API_KEY }} fail-on: block # block | quarantine | alert

One include on GitLab, from the CI/CD catalog:

.gitlab-ci.ymlinclude: - component: gitlab.com/cyberxyz/depalert/scan@1.0.1 inputs: fail_on: block

Or let the CLI detect the provider from the git remote and write the file, on GitHub, GitLab or Azure DevOps:

shell, CLI 1.4.56 or laterpip install --upgrade cyberxyz-scanner cd your-repo xyz ci init # writes the pipeline file, prints the one manual step xyz ci init --fail-on quarantine # tighten the gate

In every case the one thing that cannot be automated is adding the API key as a CI secret named XYZ_API_KEY. The key is created in the CyberXYZ dashboard with read and scan permissions only; never give a CI secret admin rights.

What it is not

DepAlert checks what the manifest says will be installed. It does not watch network traffic or processes on the runner, and it does not block lockfile drift. To gate what is actually fetched during npm install, pip install or go get, the CyberXYZ proxy goes on the runner as well; the gate and the proxy return the same verdict from the same engine. And DepAlert is not a replacement for a vulnerability scanner: scanners tell you what is known to be wrong in the tree you already have, the gate stops the next thing from getting in.

MITRE ATT&CK

Initial AccessT1195.002Compromise Software Supply Chain
ImpactT1485Data Destruction, applied to the package's own code

Recommended actions

Immediate

Search every lockfile for "faker" resolving to 6.6.6 and move to @faker-js/faker. Pin exact versions in lockfiles and commit them. Add a gate in front of CI installs so a malicious or sabotaged version fails the build instead of the test suite.

Short term: set the gate to quarantine once you have a week of baseline, so day-zero signals such as new-dependency injection, version jumps and lifecycle-hook changes also fail the build. Put the CyberXYZ proxy on the runners for installs that bypass the lockfile.

Long term: treat the registry's latest tag as an input, not a decision. What gets installed should be decided by something that has an opinion about the version, on the day it is published, whether or not anyone has written an advisory yet.


References

  1. GitHub Advisory Database, GHSA-5w9c-rv96-fr7g: Removal of functional code in faker.js
  2. BleepingComputer, Dev corrupts NPM libs 'colors' and 'faker' breaking thousands of apps (9 January 2022)
  3. NIST NVD, CVE-2021-23567, the related colors.js sabotage
  4. npm registry, faker package record, dist-tags and publish times
  5. CyberXYZ, the live blocked pull request and the CI/CD gate documentation
👋

Let's Talk

Want to learn how CyberXYZ protects your supply chain? We'd love to hear from you.