From e4d2fe025ee3dc5efa9c5548eb463b842482cc1e Mon Sep 17 00:00:00 2001 From: Zoltan Kochan Date: Mon, 8 Jun 2026 21:03:28 +0200 Subject: [PATCH] docs: clarify store trust boundary (#12268) --- .changeset/store-discl.md | 7 +++++++ cli/common-cli-options-help/src/index.ts | 2 +- installing/commands/src/install.ts | 2 +- pacquet/crates/config/src/lib.rs | 9 ++++++++- .../crates/store-dir/src/check_pkg_files_integrity.rs | 4 ++++ store/cafs/src/checkPkgFilesIntegrity.ts | 5 +++-- 6 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 .changeset/store-discl.md diff --git a/.changeset/store-discl.md b/.changeset/store-discl.md new file mode 100644 index 0000000000..d94ce8d0c5 --- /dev/null +++ b/.changeset/store-discl.md @@ -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. diff --git a/cli/common-cli-options-help/src/index.ts b/cli/common-cli-options-help/src/index.ts index 54ca17037d..4de7373d62 100644 --- a/cli/common-cli-options-help/src/index.ts +++ b/cli/common-cli-options-help/src/index.ts @@ -16,7 +16,7 @@ export const OPTIONS = { name: '--prefer-offline', }, 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 ', }, virtualStoreDir: { diff --git a/installing/commands/src/install.ts b/installing/commands/src/install.ts index 1167d6b4a3..c7771eae2b 100644 --- a/installing/commands/src/install.ts +++ b/installing/commands/src/install.ts @@ -218,7 +218,7 @@ by any dependencies, so it is an emulation of a flat node_modules', 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', }, { diff --git a/pacquet/crates/config/src/lib.rs b/pacquet/crates/config/src/lib.rs index 5a8a3ff0f7..a95c472b6d 100644 --- a/pacquet/crates/config/src/lib.rs +++ b/pacquet/crates/config/src/lib.rs @@ -387,7 +387,9 @@ pub struct Config { /// true to hoist them for you. 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::()")] pub store_dir: StoreDir, @@ -792,6 +794,9 @@ pub struct Config { /// lookup skips that verification entirely and trusts the index — a /// 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 /// `pnpm-workspace.yaml` (same `true` default as pnpm's /// `installing/deps-installer/src/install/extendInstallOptions.ts`). @@ -1152,6 +1157,8 @@ pub struct Config { /// verification gate to memoize past results in /// `/lockfile-verified.jsonl`, and by the npm verifier /// to mirror full-metadata responses for conditional GETs. + /// Share a writable cache only between mutually trusted users, + /// jobs, and processes. /// /// Mirrors pnpm's /// [`cacheDir`](https://github.com/pnpm/pnpm/blob/2a9bd897bf/config/reader/src/Config.ts#L159); diff --git a/pacquet/crates/store-dir/src/check_pkg_files_integrity.rs b/pacquet/crates/store-dir/src/check_pkg_files_integrity.rs index 37496c28bc..d3c85d9fe0 100644 --- a/pacquet/crates/store-dir/src/check_pkg_files_integrity.rs +++ b/pacquet/crates/store-dir/src/check_pkg_files_integrity.rs @@ -270,6 +270,10 @@ fn build_side_effects_maps( /// fails, so operators can see *which* package file invalidated the /// 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. /// 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 diff --git a/store/cafs/src/checkPkgFilesIntegrity.ts b/store/cafs/src/checkPkgFilesIntegrity.ts index 9e6158cd14..e9a431ecf5 100644 --- a/store/cafs/src/checkPkgFilesIntegrity.ts +++ b/store/cafs/src/checkPkgFilesIntegrity.ts @@ -168,8 +168,9 @@ function verifyFile ( } return passed } - // If a file was not edited, we are skipping integrity check. - // We assume that nobody will manually remove a file in the store and create a new one. + // Fast path for trusted stores: if metadata says the file is unchanged, skip the + // digest read. Store integrity verification detects corruption; it does not make + // a store writable by untrusted users safe. return true }