Security concepts

What is a trivial package?

Concordia University (Abdalkareem et al., FSE 2017) defined trivial packages as low-complexity dependencies under 35 lines and found they were 16.8% of studied npm packages. left-pad (March 2016), hoarders (2012), no-one-left-behind (2023), and everything (January 2024) show the operational cost. Use language natives (padStart, Array.isArray) and vendor the rest.

8 min read
In short: Concordia University (Abdalkareem et al., FSE 2017) defined trivial packages as low-complexity dependencies under 35 lines and found they were 16.8% of studied npm packages. left-pad (March 2016), hoarders (2012), no-one-left-behind (2023), and everything (January 2024) show the operational cost. Use language natives (padStart, Array.isArray) and vendor the rest.

What is a trivial package?

A trivial package is a published dependency that is both short and simple enough that you could write it yourself. Concordia University's 2017 study, Why Do Developers Use Trivial Packages? (Abdalkareem et al., FSE 2017), set the working threshold at 35 lines of code and low complexity. The authors mined more than 230,000 npm packages and 38,000 JavaScript applications and classified 16.8% of the studied packages as trivial (38,845 of 231,092).

You add a trivial package for a one-line job: pad a string, test whether a value is an array, or check process.platform. The function works. You also added a maintainer, a registry name, an unpublish policy, and every dependency that package has. That is the security cost. Closely related failures are package hijacking and dependency confusion.

Why did left-pad break so many builds in 2016?

In March 2016 Azer Koçulu unpublished 273 of his npm packages after a trademark dispute over the name kik. One of them was left-pad: eleven lines that prefix a string. Babel and a long list of applications that had never heard of Koçulu stopped installing.

module.exports = leftpad;
function leftpad(str, len, ch) {
str = String(str);
var i = -1;
if (!ch && ch !== 0) ch = " ";
len = len - str.length;
while (++i < len) {
str = ch + str;
}
return str;
}

Since ES2017 the native equivalent is String.prototype.padStart. You do not need a package for this:

"7".padStart(3, "0"); // "007"

Within hours of the unpublish, name-squatters had claimed about 230 of the vacated names. A hostile replacement in that window would have been a supply-chain incident, not just a broken build. npm later made it much harder to unpublish a package that other packages depend on. That policy is why the later mass-dependency pranks worked.

Which trivial packages are still widely installed?

The Concordia definition is about size and complexity, not fame. Several packages that meet it still sit in lockfiles across the ecosystem. Download counts below are from the npm downloads API for the week of 17-23 August 2026.

PackageWhat it doesNative alternativeWeekly downloads (Aug 2026)
isarrayTests whether a value is an array

Array.isArray (ES5, 2009; all modern browsers since July 2013)

~244 million (~967 million in the prior 30 days)
is-windowsChecks that the process is running on Windows

process.platform === "win32" (available since early Node.js)

~34 million

is-odd / is-even

Parity of an integer

n % 2 === 1 / n % 2 === 0

~1.4 million / ~0.6 million

isarray still exists as a compatibility shim for obsolete browsers and old Node. Its own readme says to call Array.isArray directly unless you must support those versions. The 2026 download numbers are almost entirely transitive: something you already depend on still lists it. That is the point. You do not choose most of the trivial packages in your tree.

Why do developers keep adding them?

Concordia's survey of 88 Node.js developers found the main reason: people want code that is "well implemented and tested." More than half of respondents described trivial packages that way. Empirically, only 45.2% of the trivial npm packages in the 2017 study had tests. 10.9% of the evaluated applications used at least one trivial package. 57.9% of surveyed developers did not consider the practice bad.

The 2021 follow-up (Helping or not helping?, Chen et al., EMSE) still found trivial packages on the order of 15-17% of npm. A 2025 sample (arXiv:2510.04495) classified 17.92% of 3,220 mined packages as trivial under the same 35-line rule, and 12.3% of those had more than a million downloads in a month. The habit did not age out.

Trivial packages also receive less maintenance, and they are slightly less likely to pin versions. After left-pad, that combination is the hijack path: an unpublished or neglected name is cheap to occupy. See package hijacking.

How do trivial packages multiply risk?

In the 2017 Concordia set, more than 40% of trivial packages had at least one dependency, and 11.5% had more than twenty. You imported eleven lines and received a tree. Every extra package.json entry is a name an attacker can confuse or hijack.

npm's post-left-pad unpublish rules (you cannot freely remove a package that others depend on) were then turned around:

  • hoarders (June 2012). An earlier "utility grab-bag" that depended on every npm module of the day (on the order of 20,000). Isaac Schlueter and others pushed back; the package was later rewritten to lazy-install instead of declaring every module as a direct dependency.
  • no-one-left-behind (January 2023). Published by Zalastax, it depended on the public registry at large. npm removed the root package and replaced it with a security holding entry. Tens of thousands of nolb-* satellites remained a cleanup problem.
  • everything (January 2024). PatrickJS / gdi2290 and collaborators published everything plus @everything-registry/* chunks that, through a tree of * dependencies, referenced every public package. Installing it was a local denial of service. Existing it was a registry denial of unpublish: maintainers could not remove their own packages while everything listed them. npm had to intervene. The policy written to stop another left-pad had become the lock.

A package that depends on "everything" is not a curiosity. It is what happens when a one-line import and a global unpublish rule share an ecosystem.

Which native alternatives should you use instead?

Before you add a package for a single function, search the language you already run:

JobPackage people reach forUse this instead
Left-pad a stringleft-pad"7".padStart(3, "0")
Right-pad a stringright-pad"7".padEnd(3, "0")
Is this an array?isarrayArray.isArray(value)
Which OS is this?

is-windows, is-linux

process.platform or os.platform()

Even / odd

is-even, is-odd

Remainder operator
Empty checkis-empty

value == null || value.length === 0 (with a real empty definition for objects)

If you target current Node and current browsers, these are not polyfills. They are the language. Vendor a five-line helper with the original license comment when you truly need the exact behavior and no native match exists.

How do you reduce trivial-package risk?

Know every dependency and what it does. Then:

  1. Ask whether your team could write it. A single function is not a package. A small set of functions is a candidate for a local module.
  2. Prefer the standard library. Raise the language baseline if an old target is the only reason for the shim.
  3. Read the source. Trivial is not the same as correct. Rewrite it if the published version is slower or sloppier than a ten-line local version.
  4. Vendor what you keep. Copy the file, keep the license, delete the registry name.
  5. Remove unused and dev-only packages from production installs. npm ci --omit=dev is the default in CI for an application.
  6. Review maintenance and ownership the same way you would for a large library. A neglected eleven-liner is easier to hijack than lodash.
  7. Alert on new trivial additions. Socket (which Arcjet uses) can flag trivial dependencies so the addition is a deliberate review, not a drive-by import.

Concordia's 35-line cutoff is a research definition, not a compliance rule. Take a liberal view of "trivial" when the alternative is another name in your lockfile. The minutes you save on the first day are not the unit of risk. The unit of risk is every future install of a name you do not control.

Frequently asked questions

What is a trivial package?

A published dependency that is short and simple enough that you could write it yourself. Concordia's 2017 study used a threshold of 35 lines of code and found 16.8% of studied npm packages met it.

What happened with left-pad?

In March 2016 Azer Koculu unpublished 273 npm packages, including eleven-line left-pad, after a trademark dispute over the name kik. Builds that depended on it failed. About 230 vacated names were squatted within hours. The native replacement is String.prototype.padStart (ES2017).

How widely is isarray still used?

The npm downloads API reported about 244 million weekly downloads for the week of 17-23 August 2026, and about 967 million in the prior 30 days. Array.isArray has been in ES5 since 2009.

When were the mass-dependency npm incidents?

hoarders in June 2012, no-one-left-behind in January 2023, and everything in January 2024. The later two abused npm's post-left-pad rule that a package with dependents cannot be freely unpublished.

Application security in your code

Protect your application with Arcjet

Get rate limits, bot detection, and attack blocking in your request handlers.