The npm chai package carries very little runtime security risk because it is a test-only assertion library that should never ship in your production bundle. Chai's job is to make test expectations readable, so its threat model is almost entirely about the supply chain and about keeping it correctly scoped as a dev dependency. This review covers what chai is, the one migration gotcha that trips teams up, and how to vet it like any other package.
What chai is and where it belongs
Chai is a BDD and TDD assertion library that pairs with test runners such as Mocha, AVA, and others. It gives you three interchangeable styles:
import { expect, assert } from 'chai';
expect(user.role).to.equal('admin');
assert.lengthOf(results, 3);
Because it only runs during tests, the single most important security control is trivial and often skipped: install it under devDependencies, not dependencies.
npm install --save-dev chai
Getting this wrong does not create a classic vulnerability, but it inflates your production dependency tree, widens the attack surface a runtime compromise could reach, and drags test-only transitive packages into deployment artifacts. When you audit a project, one of the first checks is whether chai npm is sitting in the wrong section of package.json.
The Chai 5 ESM-only change
The upgrade that surprises teams is that Chai 5 became ESM-only and dropped support for CommonJS. Chai 5 also dropped Node.js versions older than 16, and later releases raised the floor further. In practice that means this no longer works on the current major line:
// Fails on Chai 5+: CommonJS require is gone
const chai = require('chai');
You either migrate your tests to import syntax or stay on the Chai 4 line for CommonJS projects. The security-relevant part is the temptation to freeze on an old major indefinitely. Chai 4 still works, but pinning to it forever means you stop receiving any future fixes. If you cannot migrate right now, treat the pin as technical debt with a ticket, not a permanent state.
What the real risks are
Since chai does not touch the network, the filesystem in dangerous ways, or user input at runtime, its risk profile reduces to supply-chain concerns that apply to any dependency:
- Compromised release. A malicious version published to npm could add a postinstall script. Committing a lockfile and reviewing changelogs before bumping majors is your defense.
- Transitive bloat. Test tooling tends to pull deep trees. Each package there is one more maintainer you trust. Periodically prune plugins you no longer use.
- Typosquats. Packages named to look like chai plugins can slip into install commands. Copy names from the official docs rather than from a blog post or a chat message.
Continuous software composition analysis is the practical way to keep an eye on all three. It will flag a newly disclosed advisory in chai or one of its dependencies without you having to poll npm by hand.
Vetting the chai npm package before you add it
A quick, repeatable checklist works for chai the same way it does for the rest of your dev toolchain:
- Confirm it is going into
devDependencies. - Check the version you are installing against the latest published release, and read the release notes for the major you are on.
- Make sure your
package-lock.jsonor equivalent is committed so installs are reproducible. - Run an SCA scan in CI so a future advisory surfaces automatically rather than during an incident.
That is genuinely the whole story. Chai is a mature, widely used library; the discipline is in scoping and monitoring it, not in fearing the code itself.
Chai versus asserting by hand
It is worth stating why chai earns its place at all. Node's built-in assert module works, but chai's chained expectations read closer to the requirement they encode, which makes test intent obvious in review. Clear tests catch regressions, and regressions in auth or input handling are where security bugs live. A readable assertion library is a small, indirect contribution to a safer codebase. If you want to go deeper on testing discipline generally, our security academy covers building tests that actually exercise the risky paths.
FAQ
Is the npm chai package a security risk?
Not in any direct runtime sense. Chai is a test-only assertion library, so as long as it lives in devDependencies and never ships to production, its risk is limited to ordinary supply-chain concerns you manage with lockfiles and SCA scanning.
Why does require('chai') fail?
Because Chai 5 and later are ESM-only and dropped CommonJS support. Use import { expect } from 'chai' on the current major, or stay on the Chai 4 line if your project still relies on require.
Should chai be a dependency or a devDependency?
A devDependency, always. Chai only runs during tests. Placing it under regular dependencies bloats your production tree and pulls test-only packages into deployed artifacts for no benefit.
How do I keep chai secure over time?
Commit your lockfile, review release notes before bumping the major version, prune unused chai plugins, and run continuous SCA in CI so any new advisory against chai or its transitive dependencies is reported automatically.