Files
pnpm/SECURITY.md
T
Zoltan KochanandClaude Opus 5.5 6f95957063 docs: lint prose with Vale (#17238)
* docs: lint prose with Vale

Add Vale with an in-repo style, .vale/styles/pnpm, that turns the
writing rules in AGENTS.md and the Google developer documentation style
guide into lint rules. The stock Vale packages (Google, Microsoft,
RedHat, write-good) were too noisy to block on, for example 1,300
passive-voice hits in the v12 docs alone, so the rules are written for
this repo, following the GitLab model. Each one links the guideline it
enforces.

Levels:
- error, fails CI: dashes, plain-word substitutions, condescending
  words, "please", "note that", hedges, puffery, optional plurals,
  exclamation marks, ellipses, curly quotes, generic link text, product
  spellings, sentence-case headings, repeated words, double spaces,
  implementation history in comments; in changesets, bold or italic
  emphasis, internal names and release-line names.
- warning: future tense, sentences over 30 words, "instead of" and
  "so that" tails in changesets.
- suggestion: passive voice.

.vale.ini scopes the rules. Docs get every rule. Contributor Markdown
and code comments keep dashes and headings as warnings until their
roughly 8,100 em dashes are rewritten. Released changesets stay in
.changeset/ and are not rewritten, so lint-prose.mjs lints only the
changesets added since PROSE_BASE (the PR base in CI).

The prose workflow installs a pinned, checksum-verified Vale release and
runs `pnpm lint:prose`. The rest of this change rewrites the existing
docs, READMEs, contributor Markdown, and code comments to pass the
error-level rules. Only prose and comment text changed. Renamed
headings keep their anchors.

Spelling stays with cspell, which checks identifiers that Vale does not
see.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: keep prose lint from flattening meaning

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 21:48:09 +02:00

6.2 KiB

Security Policy

Supported Versions

Version Supported
12.x ✅
11.x ✅ till 2027 April 30
10.x ✅ till 2027 April 30
<= 9.x ❌

Reporting a Vulnerability

Submit your findings here: https://github.com/pnpm/pnpm/security/advisories

We do not operate a bounty program.

pacquet and pnpr

The Rust port (pnpm/) and the resolver server (pnpr/) are not production ready and are under active development. Do not report security issues in them through the security advisory process — open a regular issue in this repository instead.

Threat Model and Scope

pnpm's security boundary is filesystem permissions. We assume that the store directory, the project directory, node_modules, the lockfile, and pnpm's configuration files are only writable by parties the user already trusts. A report that assumes an attacker who already has write access to any of these locations is out of scope — at that point the trust boundary has already been crossed and the attacker can achieve code execution regardless of pnpm's behavior.

In particular:

  • The content-addressable store is not a security boundary against a write-capable local adversary. The integrity hashes recorded for each file live inside the store itself (e.g. <storeDir>/v3/files/index.db), in the same trust domain as the files they describe. Anyone who can modify a file in the store can also modify its recorded hash. Integrity verification therefore exists to detect accidental corruption (interrupted writes, bit-rot, partially fetched tarballs), not to defend against tampering by someone who can already write to the store. Optimizations such as the mtime fast path do not weaken this guarantee, because the guarantee was never tampering resistance.

  • The store must not be shared among mutually-untrusting users. A store writable by an untrusted party is equivalent to letting that party write arbitrary code into your node_modules. If you share a store across users or CI jobs, restrict write access to trusted identities via filesystem permissions.

  • Running pnpm inside a repository means trusting that repository. This is not limited to pnpm run, pnpm exec, and pnpm dlx, which execute whatever the repository specifies. A plain pnpm install also runs the project's own preinstall, install, postinstall, and prepare scripts by default; --ignore-scripts is what suppresses them. The build approval prompt (onlyBuiltDependencies / allowBuilds) gates dependencies' build scripts, not the scripts of the project you are installing.

    A report therefore does not describe a privilege escalation merely because an untrusted repository can influence what pnpm executes — in that starting position, code execution is already available to the attacker by design.

  • The workspace's extent is defined by pnpm-workspace.yaml and may reach outside the repository checkout. packages: patterns may be parent-relative (../shared), and pnpm treats the directories they name as workspace projects: it reads their manifests, records them as lockfile importers, and creates node_modules inside them. Those directories are part of the project trust domain by declaration. A hostile edit to pnpm-workspace.yaml that points the workspace outside the checkout is the same starting position as the previous bullet: whoever can change that file can already achieve code execution at install time through the configuration it carries, so this is not an escalation. The lockfile does not define the workspace — an importer entry for a path that no declared pattern matches does not cause pnpm to install into that path.

  • Some restrictions are hardening, not boundaries. Environment variable expansion in repository-controlled configuration is suppressed for request destinations (registry, proxy, pnprServer) and for auth values. That narrows silent credential exfiltration — the case that survives --ignore-scripts, and the case where a one-line config diff draws less review than a new postinstall script would. It is deliberately not a general-purpose sandbox, and a setting outside its coverage is not a vulnerability on that basis alone. What matters is what the gap grants an attacker beyond what the repository could already do without it.

    In particular, static settings in repository .npmrc or pnpm-workspace.yaml that configure network transport or TLS verification (such as strict-ssl=false, cafile, registry, or proxies) reflect standard configuration supported by npm and Yarn for legitimate enterprise and intranet deployments. Ignoring or forbidding these settings would break valid projects and diverge from upstream package managers.

The following are examples of reports we consider out of scope:

  • Tampering with store, lockfile, node_modules, or config files that the attacker can already write to.
  • Bypassing store integrity checks given pre-existing write access to the store.
  • Attacks that require the user to run pnpm with a maliciously crafted local project or environment that they did not obtain from a trusted source.
  • Repository-controlled configuration that changes what pnpm run, pnpm exec, or pnpm dlx executes, since those commands run repository-controlled code by definition.
  • Behavior that matches npm and Yarn and that we would have to diverge from the ecosystem to change. Report it upstream first; if they treat it as a vulnerability, we will follow.
  • Repository-level configuration files (.npmrc, pnpm-workspace.yaml) setting static transport or TLS options (such as strict-ssl=false or custom registry URLs) without environment variable expansion.

If you believe a report falls outside these assumptions — for example, a way to bypass a trust boundary that pnpm does enforce — include the exact privilege the attacker starts with and how pnpm escalates it. When the report is about a hardening measure that does not cover some setting, state what the attacker gains from that gap beyond what they could already do without it.