docs: clarify store trust boundary (#12268)

This commit is contained in:
Zoltan Kochan
2026-06-08 21:03:28 +02:00
committed by GitHub
parent c74d4e161a
commit e4d2fe025e
6 changed files with 24 additions and 5 deletions

View File

@@ -0,0 +1,7 @@
---
"@pnpm/cli.common-cli-options-help": patch
"@pnpm/installing.commands": patch
"pnpm": patch
---
Clarified in CLI help that the pnpm store is trusted shared state and store integrity checks are corruption detection, not a tamper boundary for untrusted store writers.

View File

@@ -16,7 +16,7 @@ export const OPTIONS = {
name: '--prefer-offline', name: '--prefer-offline',
}, },
storeDir: { storeDir: {
description: 'The directory in which all the packages are saved on the disk', description: 'The directory in which all packages are saved on disk. Use a shared store only with trusted users and jobs',
name: '--store-dir <dir>', name: '--store-dir <dir>',
}, },
virtualStoreDir: { virtualStoreDir: {

View File

@@ -218,7 +218,7 @@ by any dependencies, so it is an emulation of a flat node_modules',
name: '--ignore-workspace', name: '--ignore-workspace',
}, },
{ {
description: "If false, doesn't check whether packages in the store were mutated", description: 'If false, skips store integrity checks. These checks detect accidental corruption, not tampering by untrusted users with write access to the store',
name: '--[no-]verify-store-integrity', name: '--[no-]verify-store-integrity',
}, },
{ {

View File

@@ -387,7 +387,9 @@ pub struct Config {
/// true to hoist them for you. /// true to hoist them for you.
pub shamefully_hoist: bool, pub shamefully_hoist: bool,
/// The location where all the packages are saved on the disk. /// The location where all packages are saved on disk. Share a
/// writable store only between mutually trusted users, jobs, and
/// processes.
#[default(_code = "default_store_dir::<Host>()")] #[default(_code = "default_store_dir::<Host>()")]
pub store_dir: StoreDir, pub store_dir: StoreDir,
@@ -792,6 +794,9 @@ pub struct Config {
/// lookup skips that verification entirely and trusts the index — a /// lookup skips that verification entirely and trusts the index — a
/// missing blob is discovered lazily at link time instead. /// missing blob is discovered lazily at link time instead.
/// ///
/// This is corruption detection for a trusted store, not a tamper
/// boundary for a store writable by untrusted users or jobs.
///
/// Matches pnpm's `verifyStoreIntegrity` camelCase key in /// Matches pnpm's `verifyStoreIntegrity` camelCase key in
/// `pnpm-workspace.yaml` (same `true` default as pnpm's /// `pnpm-workspace.yaml` (same `true` default as pnpm's
/// `installing/deps-installer/src/install/extendInstallOptions.ts`). /// `installing/deps-installer/src/install/extendInstallOptions.ts`).
@@ -1152,6 +1157,8 @@ pub struct Config {
/// verification gate to memoize past results in /// verification gate to memoize past results in
/// `<cache_dir>/lockfile-verified.jsonl`, and by the npm verifier /// `<cache_dir>/lockfile-verified.jsonl`, and by the npm verifier
/// to mirror full-metadata responses for conditional GETs. /// to mirror full-metadata responses for conditional GETs.
/// Share a writable cache only between mutually trusted users,
/// jobs, and processes.
/// ///
/// Mirrors pnpm's /// Mirrors pnpm's
/// [`cacheDir`](https://github.com/pnpm/pnpm/blob/2a9bd897bf/config/reader/src/Config.ts#L159); /// [`cacheDir`](https://github.com/pnpm/pnpm/blob/2a9bd897bf/config/reader/src/Config.ts#L159);

View File

@@ -270,6 +270,10 @@ fn build_side_effects_maps(
/// fails, so operators can see *which* package file invalidated the /// fails, so operators can see *which* package file invalidated the
/// store-index row in the log. /// store-index row in the log.
/// ///
/// **Trust boundary.** This verification is for corruption detection
/// in a trusted local store. It is not a tamper boundary for a store
/// writable by untrusted users or jobs.
///
/// **Locking discipline.** The fast path (`is_modified == false`, i.e. /// **Locking discipline.** The fast path (`is_modified == false`, i.e.
/// the file's mtime is within 100 ms of the recorded `checked_at`) /// the file's mtime is within 100 ms of the recorded `checked_at`)
/// runs lock-free — it never touches the file's bytes and never /// runs lock-free — it never touches the file's bytes and never

View File

@@ -168,8 +168,9 @@ function verifyFile (
} }
return passed return passed
} }
// If a file was not edited, we are skipping integrity check. // Fast path for trusted stores: if metadata says the file is unchanged, skip the
// We assume that nobody will manually remove a file in the store and create a new one. // digest read. Store integrity verification detects corruption; it does not make
// a store writable by untrusted users safe.
return true return true
} }