feat: add support for trustPolicyExclude (#10168)

close #10164
This commit is contained in:
Ryo Matsukawa
2025-11-11 21:00:20 +09:00
committed by Zoltan Kochan
parent ce7af11c86
commit 93d49548ca
19 changed files with 349 additions and 31 deletions

View File

@@ -0,0 +1,22 @@
---
"@pnpm/plugin-commands-installation": minor
"@pnpm/resolve-dependencies": minor
"@pnpm/package-requester": minor
"@pnpm/store-controller-types": minor
"@pnpm/resolver-base": minor
"@pnpm/npm-resolver": minor
"@pnpm/core": minor
"@pnpm/config": minor
"pnpm": minor
---
Added support for `trustPolicyExclude` [#10164](https://github.com/pnpm/pnpm/issues/10164).
You can now list one or more specific packages or versions that pnpm should allow to install, even if those packages don't satisfy the trust policy requirement. For example:
```yaml
trustPolicy: no-downgrade
trustPolicyExclude:
- chokidar@4.0.3
- webpack@4.47.0 || 5.102.1
```

View File

@@ -234,6 +234,7 @@ export interface Config extends OptionsFromRootManifest {
fetchWarnTimeoutMs?: number
fetchMinSpeedKiBps?: number
trustPolicy?: TrustPolicy
trustPolicyExclude?: string[]
}
export interface ConfigWithDeprecatedSettings extends Config {

View File

@@ -115,6 +115,7 @@ export const types = Object.assign({
'strict-store-pkg-content-check': Boolean,
'strict-peer-dependencies': Boolean,
'trust-policy': ['off', 'no-downgrade'] satisfies TrustPolicy[],
'trust-policy-exclude': [String, Array],
'use-beta-cli': Boolean,
'use-node-version': String,
'use-running-store-server': Boolean,

View File

@@ -169,6 +169,7 @@ export interface StrictInstallOptions {
minimumReleaseAge?: number
minimumReleaseAgeExclude?: string[]
trustPolicy?: TrustPolicy
trustPolicyExclude?: string[]
}
export type InstallOptions =

View File

@@ -1182,6 +1182,7 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
minimumReleaseAge: opts.minimumReleaseAge,
minimumReleaseAgeExclude: opts.minimumReleaseAgeExclude,
trustPolicy: opts.trustPolicy,
trustPolicyExclude: opts.trustPolicyExclude,
}
)
if (!opts.include.optionalDependencies || !opts.include.devDependencies || !opts.include.dependencies) {

View File

@@ -210,6 +210,7 @@ async function resolveAndFetch (
alwaysTryWorkspacePackages: options.alwaysTryWorkspacePackages,
defaultTag: options.defaultTag,
trustPolicy: options.trustPolicy,
trustPolicyExclude: options.trustPolicyExclude,
publishedBy: options.publishedBy,
publishedByExclude: options.publishedByExclude,
pickLowestVersion: options.pickLowestVersion,

View File

@@ -75,6 +75,7 @@ export function rcOptionsTypes (): Record<string, unknown> {
'store-dir',
'strict-peer-dependencies',
'trust-policy',
'trust-policy-exclude',
'unsafe-perm',
'offline',
'only',

View File

@@ -64,6 +64,7 @@ export function rcOptionsTypes (): Record<string, unknown> {
'store-dir',
'strict-peer-dependencies',
'trust-policy',
'trust-policy-exclude',
'offline',
'only',
'optional',
@@ -208,6 +209,10 @@ by any dependencies, so it is an emulation of a flat node_modules',
description: "Fail when a package's trust level is downgraded (e.g., from a trusted publisher to provenance only or no trust evidence)",
name: '--trust-policy no-downgrade',
},
{
description: 'Exclude specific packages from trust policy checks',
name: '--trust-policy-exclude <package-spec>',
},
{
description: 'Starts a store server in the background. The store server will keep running after installation is done. To stop the store server, run `pnpm server stop`',
name: '--use-store-server',

View File

@@ -184,6 +184,7 @@ export interface ResolutionContext {
maximumPublishedBy?: Date
publishedByExclude?: PackageVersionPolicy
trustPolicy?: TrustPolicy
trustPolicyExclude?: PackageVersionPolicy
}
export interface MissingPeerInfo {
@@ -1339,6 +1340,7 @@ async function resolveDependency (
: options.parentPkg.rootDir,
skipFetch: ctx.dryRun,
trustPolicy: ctx.trustPolicy,
trustPolicyExclude: ctx.trustPolicyExclude,
update: options.update,
workspacePackages: ctx.workspacePackages,
supportedArchitectures: options.supportedArchitectures,

View File

@@ -140,6 +140,7 @@ export interface ResolveDependenciesOptions {
minimumReleaseAge?: number
minimumReleaseAgeExclude?: string[]
trustPolicy?: TrustPolicy
trustPolicyExclude?: string[]
}
export interface ResolveDependencyTreeResult {
@@ -203,6 +204,7 @@ export async function resolveDependencyTree<T> (
maximumPublishedBy: opts.minimumReleaseAge ? new Date(Date.now() - opts.minimumReleaseAge * 60 * 1000) : undefined,
publishedByExclude: opts.minimumReleaseAgeExclude ? createPublishedByExclude(opts.minimumReleaseAgeExclude) : undefined,
trustPolicy: opts.trustPolicy,
trustPolicyExclude: opts.trustPolicyExclude ? createTrustPolicyExclude(opts.trustPolicyExclude) : undefined,
}
function createPublishedByExclude (patterns: string[]): PackageVersionPolicy {
@@ -214,6 +216,15 @@ export async function resolveDependencyTree<T> (
}
}
function createTrustPolicyExclude (patterns: string[]): PackageVersionPolicy {
try {
return createPackageVersionPolicy(patterns)
} catch (err) {
if (!err || typeof err !== 'object' || !('message' in err)) throw err
throw new PnpmError('INVALID_TRUST_POLICY_EXCLUDE', `Invalid value in trustPolicyExclude: ${err.message as string}`)
}
}
const resolveArgs: ImporterToResolve[] = importers.map((importer) => {
const projectSnapshot = opts.wantedLockfile.importers[importer.id]
// This may be optimized.

201
pnpm-lock.yaml generated
View File

@@ -1163,7 +1163,7 @@ importers:
dependencies:
'@pnpm/workspace.find-packages':
specifier: 'catalog:'
version: 1000.0.25(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.0(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)
version: 1000.0.25(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.1(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)
'@pnpm/workspace.read-manifest':
specifier: 'catalog:'
version: 1000.1.5
@@ -7349,6 +7349,9 @@ importers:
specifier: 'catalog:'
version: 3.0.0
devDependencies:
'@pnpm/config.version-policy':
specifier: workspace:*
version: link:../../config/version-policy
'@pnpm/fetch':
specifier: workspace:*
version: link:../../network/fetch
@@ -9951,6 +9954,12 @@ packages:
peerDependencies:
'@pnpm/logger': '>=1001.0.0 <1002.0.0'
'@pnpm/core-loggers@1001.0.5':
resolution: {integrity: sha512-aigaE/Bm/3aYOokxLlsNfZhPRsuQk54/9wT0+ie4SnTRBZ+Zh+BMYQ1/Uvv0RpXknFs2zmfcoX1fpvAvrt6khw==}
engines: {node: '>=18.12'}
peerDependencies:
'@pnpm/logger': '>=1001.0.0 <1002.0.0'
'@pnpm/create-cafs-store@1000.0.14':
resolution: {integrity: sha512-95OczT9/zsJea3Nm6x9ovUD04GoLKYHnSCKUhe5VFx7ckUxW8DUrFJynl94Tx+kdf7u7zXBIlwhhHJEplDaNTg==}
engines: {node: '>=18.12'}
@@ -9963,6 +9972,12 @@ packages:
peerDependencies:
'@pnpm/logger': '>=1001.0.0 <1002.0.0'
'@pnpm/create-cafs-store@1000.0.21':
resolution: {integrity: sha512-FSNdaKCvdOcJu5cOJ5dx1hSIGBDPAnxHVrdh87bdPHwsTPxqK8n/9b+UbROHDeFJsIccB0yRXPEp2+0iB5xBKw==}
engines: {node: '>=18.12'}
peerDependencies:
'@pnpm/logger': '>=1001.0.0 <1002.0.0'
'@pnpm/crypto.hash@1000.1.1':
resolution: {integrity: sha512-lb5kwXaOXdIW/4bkLLmtM9HEVRvp2eIvp+TrdawcPoaptgA/5f0/sRG0P52BF8dFqeNDj+1tGdqH89WQEqJnxA==}
engines: {node: '>=18.12'}
@@ -10015,6 +10030,10 @@ packages:
resolution: {integrity: sha512-6cV8WWS5/bTEwkk2RF9WF/QeRxDqsN2rJ+b6OnQYT3jiUqWlAjdESu0EzOX4LkGQiJTOQS4k34axM3FcUKdIaw==}
engines: {node: '>=18.12'}
'@pnpm/exec.pkg-requires-build@1000.0.12':
resolution: {integrity: sha512-8J8CmroksVJcTDfUOdQt3vARjdiJWzwCgKw7opxjFlg1ZjFR413dptc7njhWIxsMVvSaaBOaS7FY55y6wpUj+g==}
engines: {node: '>=18.12'}
'@pnpm/exec.pkg-requires-build@1000.0.8':
resolution: {integrity: sha512-8Mx71nPcUEJpLVzl4k/+Yu5Mir8JLg4oWEImkMfLKd9orU/F7A5FIHTeLw4RAnK0MummjmXPwj8UMQgOxkq2eA==}
engines: {node: '>=18.12'}
@@ -10037,6 +10056,10 @@ packages:
resolution: {integrity: sha512-+oL/a1no20hJvfEX4y4NSPufULLSLvSS9m+rG3qmVCXZ9q8izOKUaHVnPgfRSFC4LoN1N+lewBmp5MTKALoy0A==}
engines: {node: '>=18.12'}
'@pnpm/fetcher-base@1001.0.3':
resolution: {integrity: sha512-sMu5Iw9VaRyvt2cTXR/xVnSlch48hCl3wzolyWfB74VPVlQTWqP6jqGdrxM07BL2hB8XY7J8UYKDOAFbObGbxg==}
engines: {node: '>=18.12'}
'@pnpm/fetching-types@1000.1.0':
resolution: {integrity: sha512-0JFRtWH/6Pwsl9Q9CwxHpCxsoaaTr4cYbL4moMiVYnllg8yeJSU3V5S0gPsAlIdhHfjBVNfwMIM99pICzic33Q==}
engines: {node: '>=18.12'}
@@ -10055,12 +10078,24 @@ packages:
peerDependencies:
'@pnpm/logger': '>=1001.0.0 <1002.0.0'
'@pnpm/fs.hard-link-dir@1000.0.3':
resolution: {integrity: sha512-S7uII7TiCXTQVUxFNOgZACUTz7KmEWd88EhxEmeWE5RpdZPYSCR+rTBNdJb1WqHeIrwTkTYQS63/DQPkB0cbWA==}
engines: {node: '>=18.12'}
peerDependencies:
'@pnpm/logger': '>=1001.0.0 <1002.0.0'
'@pnpm/fs.indexed-pkg-importer@1000.1.14':
resolution: {integrity: sha512-sInsw499KKeuirsv8U4jnUIGPaOqsiO9lYgiyEKKmYnDyEVb4m1XENCOc9sC0/z3Pubz3cfTf0rUje6wXzSKCQ==}
engines: {node: '>=18.12'}
peerDependencies:
'@pnpm/logger': '>=1001.0.0 <1002.0.0'
'@pnpm/fs.indexed-pkg-importer@1000.1.15':
resolution: {integrity: sha512-6lE9LlgI/q4GXhcYmWyzXZe14bvuGqZja+ICNYqznHvMWa4qw3oQZazRgHykj96P228+iKBJ969otjxEvJgf+g==}
engines: {node: '>=18.12'}
peerDependencies:
'@pnpm/logger': '>=1001.0.0 <1002.0.0'
'@pnpm/fs.indexed-pkg-importer@1000.1.8':
resolution: {integrity: sha512-VwsjBhAyW+5TQO6Ndon1y8kyvSLQJyyWzwNRENQN+UkbrybsjNXHX1vcMV4Li/+pQ0VBYFVxYl/cx+EkU8H9hQ==}
engines: {node: '>=18.12'}
@@ -10309,6 +10344,10 @@ packages:
resolution: {integrity: sha512-7JljhfFFf/qUVi/Nzfq4stBpfSt/JJp5xYx0nAJ6/cagAqiVeYP/chawj7iT3jhhpESclAToTi0NjyiTTzK4VQ==}
engines: {node: '>=18.12'}
'@pnpm/resolver-base@1005.2.0':
resolution: {integrity: sha512-bzNgwhNH6K5jHea65DZbHbNwWYI1j2Mp8ozZHahwT4+nxmIu59drqkoylsjk2tttS0jUJmMnWp6ncwuR/BiHiA==}
engines: {node: '>=18.12'}
'@pnpm/resolving.jsr-specifier-parser@1000.0.0':
resolution: {integrity: sha512-/61cFu7EJcrXCJtqo9cjaEcuvtUYWOZQE0o7CrYL0S15lN7gSbUMD19y/n0eG4rBxyRJg3U+fnj43f+mvREL/A==}
engines: {node: '>=18.12'}
@@ -10345,6 +10384,10 @@ packages:
resolution: {integrity: sha512-8wk06DIwhTf16SRJ51PKuWzu/Cm8KuLopQmx+aIUkoCGS+EjaeVtX8oG8lI+MfgSrsHStczRozb3WLI9m/BPTA==}
engines: {node: '>=18.12'}
'@pnpm/store-controller-types@1004.2.0':
resolution: {integrity: sha512-JEsQXE+N75SJpSPnQKLv5nFUGkrVimNgiFnRczCPefDp3PLGMmiK04z2l3U1hRJiVRot5FQk/pbjMBIFcu8Pzw==}
engines: {node: '>=18.12'}
'@pnpm/store-path@1000.0.2':
resolution: {integrity: sha512-Ab2RJUnMb0ZP7rRTP9mr+KUSeoWjozNbd9gqC7ZYptHUlPohpVbjBY2xeppApw6GVzHLWPB3hIyXXz7qylnHuQ==}
engines: {node: '>=18.12'}
@@ -10357,12 +10400,22 @@ packages:
resolution: {integrity: sha512-q/NUuyBVF4PopTdwtttoPOPEq8LOuTLkRV13jvrJ/+DaorebJjoKQPduVwIP6DxArzgrkpM2XyGgYC7WN/xhpg==}
engines: {node: '>=18.12'}
'@pnpm/store.cafs@1000.0.20':
resolution: {integrity: sha512-EAWzqyth3NYnLngZspV+s4G/+9aIa8v7VL8mEPMBJkC/2f+WwWwPlwZNwDnCdKUErZveRkQ2ZHcmOOGfrWoTng==}
engines: {node: '>=18.12'}
'@pnpm/symlink-dependency@1000.0.12':
resolution: {integrity: sha512-/rQJ7VpZwPnlj72FQ6UuZyGJJrcwbRAkEE4WqW+Bpw9vHPGGE6KXm8aHnTC3JFhdoQ22ekEL7/2tgMJ7e/D4iQ==}
engines: {node: '>=18.12'}
peerDependencies:
'@pnpm/logger': '>=1001.0.0 <1002.0.0'
'@pnpm/symlink-dependency@1000.0.13':
resolution: {integrity: sha512-EljOWLPnzHmLO/ibPPW1sqpwFoCrllHrXJtLu/g9xLq6zOCGXuv0EPtcgVH98AJHFwddR2K7j+U3wbXAcVqXMQ==}
engines: {node: '>=18.12'}
peerDependencies:
'@pnpm/logger': '>=1001.0.0 <1002.0.0'
'@pnpm/tabtab@0.5.4':
resolution: {integrity: sha512-bWLDlHsBlgKY/05wDN/V3ETcn5G2SV/SiA2ZmNvKGGlmVX4G5li7GRDhHcgYvHJHyJ8TUStqg2xtHmCs0UbAbg==}
engines: {node: '>=18'}
@@ -10393,6 +10446,10 @@ packages:
resolution: {integrity: sha512-UvDTCxnbyqkTg2X0dBOuZ4IdFJ8g4UFu0Ybv/5/cZAxCWVhNl1hC/Xc9hR4tZrlBL0NRFePLRhO/iw9LmA1lbw==}
engines: {node: '>=18.12'}
'@pnpm/types@1001.0.0':
resolution: {integrity: sha512-9P7I8Zv8hvAO81+D5KVmwveH4nmxhBNFEEeb77YYPV72bkyqzKTR6I8OGEs7TNZ6gPHufF+lIyBVEqO6SMFpJA==}
engines: {node: '>=18.12'}
'@pnpm/util.lex-comparator@3.0.2':
resolution: {integrity: sha512-blFO4Ws97tWv/SNE6N39ZdGmZBrocXnBOfVp0ln4kELmns4pGPZizqyRtR8EjfOLMLstbmNCTReBoDvLz1isVg==}
engines: {node: '>=18.12'}
@@ -10408,6 +10465,12 @@ packages:
peerDependencies:
'@pnpm/logger': '>=1001.0.0 <1002.0.0'
'@pnpm/worker@1000.3.1':
resolution: {integrity: sha512-5wcfM0BC2Yt1Wj4H3zNGliWTCnh2DhxSF219nPcXy3EPptVT8L7Q+5y0vxNdpktJikYRkT5GFRhkowyz0dQlaw==}
engines: {node: '>=18.12'}
peerDependencies:
'@pnpm/logger': '>=1001.0.0 <1002.0.0'
'@pnpm/workspace.find-packages@1000.0.25':
resolution: {integrity: sha512-dKXeM46nSXKOzIIvofAhrcZqivxeJIqG27MX2nQoYYtccdJw6IBWozPqDJIPw0V3WLt9DAEQOqooEasbBmB5wg==}
engines: {node: '>=18.12'}
@@ -17035,11 +17098,11 @@ snapshots:
- supports-color
- typanion
'@pnpm/cli-utils@1000.1.5(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.0(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)':
'@pnpm/cli-utils@1000.1.5(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.1(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)':
dependencies:
'@pnpm/cli-meta': 1000.0.8
'@pnpm/config': 1003.1.1(@pnpm/logger@1001.0.0)
'@pnpm/config.deps-installer': 1000.0.5(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.0(@pnpm/logger@1001.0.0)(@types/node@22.15.29))
'@pnpm/config.deps-installer': 1000.0.5(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.1(@pnpm/logger@1001.0.0)(@types/node@22.15.29))
'@pnpm/default-reporter': 1002.0.1(@pnpm/logger@1001.0.0)
'@pnpm/error': 1000.0.2
'@pnpm/logger': 1001.0.0
@@ -17047,7 +17110,7 @@ snapshots:
'@pnpm/package-is-installable': 1000.0.10(@pnpm/logger@1001.0.0)
'@pnpm/pnpmfile': 1001.2.2(@pnpm/logger@1001.0.0)
'@pnpm/read-project-manifest': 1000.0.11
'@pnpm/store-connection-manager': 1002.0.3(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.0(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)
'@pnpm/store-connection-manager': 1002.0.3(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.1(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)
'@pnpm/types': 1000.6.0
chalk: 4.1.2
load-json-file: 6.2.0
@@ -17076,16 +17139,16 @@ snapshots:
- supports-color
- typanion
'@pnpm/client@1000.0.19(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.0(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)':
'@pnpm/client@1000.0.19(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.1(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)':
dependencies:
'@pnpm/default-resolver': 1002.0.2(@pnpm/logger@1001.0.0)
'@pnpm/directory-fetcher': 1000.1.7(@pnpm/logger@1001.0.0)
'@pnpm/fetch': 1000.2.2(@pnpm/logger@1001.0.0)
'@pnpm/fetching-types': 1000.1.0
'@pnpm/git-fetcher': 1001.0.8(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.0(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)
'@pnpm/git-fetcher': 1001.0.8(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.1(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)
'@pnpm/network.auth-header': 1000.0.3
'@pnpm/resolver-base': 1003.0.1
'@pnpm/tarball-fetcher': 1001.0.8(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.0(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)
'@pnpm/tarball-fetcher': 1001.0.8(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.1(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)
'@pnpm/types': 1000.6.0
ramda: '@pnpm/ramda@0.28.1'
transitivePeerDependencies:
@@ -17128,7 +17191,7 @@ snapshots:
- domexception
- supports-color
'@pnpm/config.deps-installer@1000.0.5(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.0(@pnpm/logger@1001.0.0)(@types/node@22.15.29))':
'@pnpm/config.deps-installer@1000.0.5(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.1(@pnpm/logger@1001.0.0)(@types/node@22.15.29))':
dependencies:
'@pnpm/config.config-writer': 1000.0.5
'@pnpm/core-loggers': 1001.0.1(@pnpm/logger@1001.0.0)
@@ -17137,7 +17200,7 @@ snapshots:
'@pnpm/logger': 1001.0.0
'@pnpm/network.auth-header': 1000.0.3
'@pnpm/npm-resolver': 1004.0.1(@pnpm/logger@1001.0.0)
'@pnpm/package-store': 1002.0.4(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.0(@pnpm/logger@1001.0.0)(@types/node@22.15.29))
'@pnpm/package-store': 1002.0.4(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.1(@pnpm/logger@1001.0.0)(@types/node@22.15.29))
'@pnpm/parse-wanted-dependency': 1001.0.0
'@pnpm/pick-registry-for-package': 1000.0.8
'@pnpm/read-modules-dir': 1000.0.0
@@ -17200,6 +17263,11 @@ snapshots:
'@pnpm/logger': 1001.0.0
'@pnpm/types': 1000.9.0
'@pnpm/core-loggers@1001.0.5(@pnpm/logger@1001.0.0)':
dependencies:
'@pnpm/logger': 1001.0.0
'@pnpm/types': 1001.0.0
'@pnpm/create-cafs-store@1000.0.14(@pnpm/logger@1001.0.0)':
dependencies:
'@pnpm/exec.pkg-requires-build': 1000.0.8
@@ -17224,6 +17292,18 @@ snapshots:
path-temp: 2.1.0
ramda: '@pnpm/ramda@0.28.1'
'@pnpm/create-cafs-store@1000.0.21(@pnpm/logger@1001.0.0)':
dependencies:
'@pnpm/exec.pkg-requires-build': 1000.0.12
'@pnpm/fetcher-base': 1001.0.3
'@pnpm/fs.indexed-pkg-importer': 1000.1.15(@pnpm/logger@1001.0.0)
'@pnpm/logger': 1001.0.0
'@pnpm/store-controller-types': 1004.2.0
'@pnpm/store.cafs': 1000.0.20
mem: 8.1.1
path-temp: 2.1.0
ramda: '@pnpm/ramda@0.28.1'
'@pnpm/crypto.hash@1000.1.1':
dependencies:
'@pnpm/crypto.polyfill': 1000.1.0
@@ -17313,6 +17393,10 @@ snapshots:
dependencies:
'@pnpm/types': 1000.9.0
'@pnpm/exec.pkg-requires-build@1000.0.12':
dependencies:
'@pnpm/types': 1001.0.0
'@pnpm/exec.pkg-requires-build@1000.0.8':
dependencies:
'@pnpm/types': 1000.6.0
@@ -17348,6 +17432,12 @@ snapshots:
'@pnpm/types': 1000.9.0
'@types/ssri': 7.1.5
'@pnpm/fetcher-base@1001.0.3':
dependencies:
'@pnpm/resolver-base': 1005.2.0
'@pnpm/types': 1001.0.0
'@types/ssri': 7.1.5
'@pnpm/fetching-types@1000.1.0':
dependencies:
'@zkochan/retry': 0.2.0
@@ -17373,6 +17463,13 @@ snapshots:
'@pnpm/graceful-fs': 1000.0.1
'@pnpm/logger': 1001.0.0
'@pnpm/fs.hard-link-dir@1000.0.3(@pnpm/logger@1001.0.0)':
dependencies:
'@pnpm/graceful-fs': 1000.0.1
'@pnpm/logger': 1001.0.0
path-temp: 2.1.0
rename-overwrite: 6.0.3
'@pnpm/fs.indexed-pkg-importer@1000.1.14(@pnpm/logger@1001.0.0)':
dependencies:
'@pnpm/core-loggers': 1001.0.4(@pnpm/logger@1001.0.0)
@@ -17388,6 +17485,21 @@ snapshots:
rename-overwrite: 6.0.3
sanitize-filename: 1.6.3
'@pnpm/fs.indexed-pkg-importer@1000.1.15(@pnpm/logger@1001.0.0)':
dependencies:
'@pnpm/core-loggers': 1001.0.5(@pnpm/logger@1001.0.0)
'@pnpm/graceful-fs': 1000.0.1
'@pnpm/logger': 1001.0.0
'@pnpm/store-controller-types': 1004.2.0
'@reflink/reflink': 0.1.19
'@zkochan/rimraf': 3.0.2
fs-extra: 11.3.0
make-empty-dir: 3.0.2
p-limit: 3.1.0
path-temp: 2.1.0
rename-overwrite: 6.0.3
sanitize-filename: 1.6.3
'@pnpm/fs.indexed-pkg-importer@1000.1.8(@pnpm/logger@1001.0.0)':
dependencies:
'@pnpm/core-loggers': 1001.0.1(@pnpm/logger@1001.0.0)
@@ -17424,13 +17536,13 @@ snapshots:
- supports-color
- typanion
'@pnpm/git-fetcher@1001.0.8(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.0(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)':
'@pnpm/git-fetcher@1001.0.8(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.1(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)':
dependencies:
'@pnpm/fetcher-base': 1000.0.11
'@pnpm/fs.packlist': 2.0.0
'@pnpm/logger': 1001.0.0
'@pnpm/prepare-package': 1000.0.16(@pnpm/logger@1001.0.0)(typanion@3.14.0)
'@pnpm/worker': 1000.3.0(@pnpm/logger@1001.0.0)(@types/node@22.15.29)
'@pnpm/worker': 1000.3.1(@pnpm/logger@1001.0.0)(@types/node@22.15.29)
'@zkochan/rimraf': 3.0.2
execa: safe-execa@0.1.2
transitivePeerDependencies:
@@ -17747,7 +17859,7 @@ snapshots:
semver: 7.7.2
ssri: 10.0.5
'@pnpm/package-requester@1004.0.2(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.0(@pnpm/logger@1001.0.0)(@types/node@22.15.29))':
'@pnpm/package-requester@1004.0.2(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.1(@pnpm/logger@1001.0.0)(@types/node@22.15.29))':
dependencies:
'@pnpm/core-loggers': 1001.0.1(@pnpm/logger@1001.0.0)
'@pnpm/dependency-path': 1000.0.9
@@ -17762,7 +17874,7 @@ snapshots:
'@pnpm/store-controller-types': 1003.0.2
'@pnpm/store.cafs': 1000.0.13
'@pnpm/types': 1000.6.0
'@pnpm/worker': 1000.3.0(@pnpm/logger@1001.0.0)(@types/node@22.15.29)
'@pnpm/worker': 1000.3.1(@pnpm/logger@1001.0.0)(@types/node@22.15.29)
p-defer: 3.0.0
p-limit: 3.1.0
p-queue: 6.6.2
@@ -17787,17 +17899,17 @@ snapshots:
ramda: '@pnpm/ramda@0.28.1'
ssri: 10.0.5
'@pnpm/package-store@1002.0.4(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.0(@pnpm/logger@1001.0.0)(@types/node@22.15.29))':
'@pnpm/package-store@1002.0.4(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.1(@pnpm/logger@1001.0.0)(@types/node@22.15.29))':
dependencies:
'@pnpm/create-cafs-store': 1000.0.14(@pnpm/logger@1001.0.0)
'@pnpm/fetcher-base': 1000.0.11
'@pnpm/logger': 1001.0.0
'@pnpm/package-requester': 1004.0.2(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.0(@pnpm/logger@1001.0.0)(@types/node@22.15.29))
'@pnpm/package-requester': 1004.0.2(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.1(@pnpm/logger@1001.0.0)(@types/node@22.15.29))
'@pnpm/resolver-base': 1003.0.1
'@pnpm/store-controller-types': 1003.0.2
'@pnpm/store.cafs': 1000.0.13
'@pnpm/types': 1000.6.0
'@pnpm/worker': 1000.3.0(@pnpm/logger@1001.0.0)(@types/node@22.15.29)
'@pnpm/worker': 1000.3.1(@pnpm/logger@1001.0.0)(@types/node@22.15.29)
'@zkochan/rimraf': 3.0.2
load-json-file: 6.2.0
ramda: '@pnpm/ramda@0.28.1'
@@ -17919,6 +18031,10 @@ snapshots:
dependencies:
'@pnpm/types': 1000.9.0
'@pnpm/resolver-base@1005.2.0':
dependencies:
'@pnpm/types': 1001.0.0
'@pnpm/resolving.jsr-specifier-parser@1000.0.0':
dependencies:
'@pnpm/error': 1000.0.2
@@ -17963,14 +18079,14 @@ snapshots:
- supports-color
- typanion
'@pnpm/store-connection-manager@1002.0.3(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.0(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)':
'@pnpm/store-connection-manager@1002.0.3(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.1(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)':
dependencies:
'@pnpm/cli-meta': 1000.0.8
'@pnpm/client': 1000.0.19(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.0(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)
'@pnpm/client': 1000.0.19(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.1(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)
'@pnpm/config': 1003.1.1(@pnpm/logger@1001.0.0)
'@pnpm/error': 1000.0.2
'@pnpm/logger': 1001.0.0
'@pnpm/package-store': 1002.0.4(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.0(@pnpm/logger@1001.0.0)(@types/node@22.15.29))
'@pnpm/package-store': 1002.0.4(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.1(@pnpm/logger@1001.0.0)(@types/node@22.15.29))
'@pnpm/server': 1001.0.4(@pnpm/logger@1001.0.0)
'@pnpm/store-path': 1000.0.2
'@zkochan/diable': 1.0.2
@@ -17994,6 +18110,12 @@ snapshots:
'@pnpm/resolver-base': 1005.1.0
'@pnpm/types': 1000.9.0
'@pnpm/store-controller-types@1004.2.0':
dependencies:
'@pnpm/fetcher-base': 1001.0.3
'@pnpm/resolver-base': 1005.2.0
'@pnpm/types': 1001.0.0
'@pnpm/store-path@1000.0.2':
dependencies:
'@pnpm/constants': 1001.1.0
@@ -18029,6 +18151,18 @@ snapshots:
ssri: 10.0.5
strip-bom: 4.0.0
'@pnpm/store.cafs@1000.0.20':
dependencies:
'@pnpm/fetcher-base': 1001.0.3
'@pnpm/graceful-fs': 1000.0.1
'@pnpm/store-controller-types': 1004.2.0
'@zkochan/rimraf': 3.0.2
is-gzip: 2.0.0
p-limit: 3.1.0
rename-overwrite: 6.0.3
ssri: 10.0.5
strip-bom: 4.0.0
'@pnpm/symlink-dependency@1000.0.12(@pnpm/logger@1001.0.0)':
dependencies:
'@pnpm/core-loggers': 1001.0.4(@pnpm/logger@1001.0.0)
@@ -18036,6 +18170,13 @@ snapshots:
'@pnpm/types': 1000.9.0
symlink-dir: 6.0.5
'@pnpm/symlink-dependency@1000.0.13(@pnpm/logger@1001.0.0)':
dependencies:
'@pnpm/core-loggers': 1001.0.5(@pnpm/logger@1001.0.0)
'@pnpm/logger': 1001.0.0
'@pnpm/types': 1001.0.0
symlink-dir: 6.0.5
'@pnpm/tabtab@0.5.4':
dependencies:
debug: 4.4.1
@@ -18067,7 +18208,7 @@ snapshots:
- supports-color
- typanion
'@pnpm/tarball-fetcher@1001.0.8(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.0(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)':
'@pnpm/tarball-fetcher@1001.0.8(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.1(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)':
dependencies:
'@pnpm/core-loggers': 1001.0.1(@pnpm/logger@1001.0.0)
'@pnpm/error': 1000.0.2
@@ -18077,7 +18218,7 @@ snapshots:
'@pnpm/graceful-fs': 1000.0.0
'@pnpm/logger': 1001.0.0
'@pnpm/prepare-package': 1000.0.16(@pnpm/logger@1001.0.0)(typanion@3.14.0)
'@pnpm/worker': 1000.3.0(@pnpm/logger@1001.0.0)(@types/node@22.15.29)
'@pnpm/worker': 1000.3.1(@pnpm/logger@1001.0.0)(@types/node@22.15.29)
'@zkochan/retry': 0.2.0
lodash.throttle: 4.1.1
p-map-values: 1.0.0
@@ -18106,6 +18247,8 @@ snapshots:
'@pnpm/types@1000.9.0': {}
'@pnpm/types@1001.0.0': {}
'@pnpm/util.lex-comparator@3.0.2': {}
'@pnpm/which@3.0.1':
@@ -18132,18 +18275,18 @@ snapshots:
transitivePeerDependencies:
- '@types/node'
'@pnpm/worker@1000.3.0(@pnpm/logger@1001.0.0)(@types/node@22.15.29)':
'@pnpm/worker@1000.3.1(@pnpm/logger@1001.0.0)(@types/node@22.15.29)':
dependencies:
'@pnpm/cafs-types': 1000.0.0
'@pnpm/create-cafs-store': 1000.0.20(@pnpm/logger@1001.0.0)
'@pnpm/create-cafs-store': 1000.0.21(@pnpm/logger@1001.0.0)
'@pnpm/crypto.polyfill': 1000.1.0
'@pnpm/error': 1000.0.5
'@pnpm/exec.pkg-requires-build': 1000.0.11
'@pnpm/fs.hard-link-dir': 1000.0.2(@pnpm/logger@1001.0.0)
'@pnpm/exec.pkg-requires-build': 1000.0.12
'@pnpm/fs.hard-link-dir': 1000.0.3(@pnpm/logger@1001.0.0)
'@pnpm/graceful-fs': 1000.0.1
'@pnpm/logger': 1001.0.0
'@pnpm/store.cafs': 1000.0.19
'@pnpm/symlink-dependency': 1000.0.12(@pnpm/logger@1001.0.0)
'@pnpm/store.cafs': 1000.0.20
'@pnpm/symlink-dependency': 1000.0.13(@pnpm/logger@1001.0.0)
'@rushstack/worker-pool': 0.4.9(@types/node@22.15.29)
is-windows: 1.0.2
load-json-file: 6.2.0
@@ -18166,9 +18309,9 @@ snapshots:
- supports-color
- typanion
'@pnpm/workspace.find-packages@1000.0.25(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.0(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)':
'@pnpm/workspace.find-packages@1000.0.25(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.1(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)':
dependencies:
'@pnpm/cli-utils': 1000.1.5(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.0(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)
'@pnpm/cli-utils': 1000.1.5(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.3.1(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)
'@pnpm/constants': 1001.1.0
'@pnpm/fs.find-packages': 1000.0.11
'@pnpm/logger': 1001.0.0

View File

@@ -524,3 +524,27 @@ test('install does not fail when the trust evidence of a package is downgraded b
expect(result.status).toBe(0)
project.has('@pnpm/e2e.test-provenance')
})
test('install does not fail when the trust evidence of a package is downgraded but it is in trust-policy-exclude', async () => {
const project = prepare()
const result = execPnpmSync([
'add',
'@pnpm/e2e.test-provenance@0.0.5',
'--trust-policy=no-downgrade',
'--trust-policy-exclude=@pnpm/e2e.test-provenance@0.0.5',
])
expect(result.status).toBe(0)
project.has('@pnpm/e2e.test-provenance')
})
test('install does not fail when the trust evidence of a package is downgraded but the package name is in trust-policy-exclude', async () => {
const project = prepare()
const result = execPnpmSync([
'add',
'@pnpm/e2e.test-provenance@0.0.5',
'--trust-policy=no-downgrade',
'--trust-policy-exclude=@pnpm/e2e.test-provenance',
])
expect(result.status).toBe(0)
project.has('@pnpm/e2e.test-provenance')
})

View File

@@ -67,6 +67,7 @@
"@pnpm/logger": "catalog:"
},
"devDependencies": {
"@pnpm/config.version-policy": "workspace:*",
"@pnpm/fetch": "workspace:*",
"@pnpm/logger": "workspace:*",
"@pnpm/npm-resolver": "workspace:*",

View File

@@ -188,6 +188,7 @@ export type ResolveFromNpmOptions = {
publishedByExclude?: PackageVersionPolicy
pickLowestVersion?: boolean
trustPolicy?: TrustPolicy
trustPolicyExclude?: PackageVersionPolicy
dryRun?: boolean
lockfileDir?: string
preferredVersions?: PreferredVersions
@@ -308,7 +309,7 @@ async function resolveNpm (
throw new NoMatchingVersionError({ wantedDependency, packageMeta: meta, registry })
} else if (opts.trustPolicy === 'no-downgrade') {
assertMetaHasTime(meta)
failIfTrustDowngraded(meta, pickedPackage.version)
failIfTrustDowngraded(meta, pickedPackage.version, opts.trustPolicyExclude)
}
const workspacePkgsMatchingName = workspacePackages?.get(pickedPackage.name)

View File

@@ -1,5 +1,6 @@
import { PnpmError } from '@pnpm/error'
import { type PackageInRegistry, type PackageMetaWithTime } from '@pnpm/registry.types'
import { type PackageVersionPolicy } from '@pnpm/types'
type TrustEvidence = 'provenance' | 'trustedPublisher'
@@ -10,8 +11,19 @@ const TRUST_RANK = {
export function failIfTrustDowngraded (
meta: PackageMetaWithTime,
version: string
version: string,
trustPolicyExclude?: PackageVersionPolicy
): void {
if (trustPolicyExclude) {
const excludeResult = trustPolicyExclude(meta.name)
if (excludeResult === true) {
return
}
if (Array.isArray(excludeResult) && excludeResult.includes(version)) {
return
}
}
const versionPublishedAt = meta.time[version]
if (!versionPublishedAt) {
throw new PnpmError(

View File

@@ -1,4 +1,5 @@
import { type PackageInRegistry, type PackageMetaWithTime } from '@pnpm/registry.types'
import { createPackageVersionPolicy } from '@pnpm/config.version-policy'
import { getTrustEvidence, failIfTrustDowngraded } from '../src/trustChecks.js'
describe('getTrustEvidence', () => {
@@ -405,3 +406,88 @@ describe('failIfTrustDowngraded', () => {
}).toThrow('Missing time')
})
})
describe('failIfTrustDowngraded with trustPolicyExclude', () => {
test('allows downgrade when package@version is in exclude list', () => {
const meta: PackageMetaWithTime = {
name: 'foo',
'dist-tags': { latest: '3.0.0' },
versions: {
'2.0.0': {
name: 'foo',
version: '2.0.0',
dist: {
shasum: 'def456',
tarball: 'https://registry.example.com/foo/-/foo-2.0.0.tgz',
attestations: {
provenance: {
predicateType: 'https://slsa.dev/provenance/v1',
},
},
},
},
'3.0.0': {
name: 'foo',
version: '3.0.0',
dist: {
shasum: 'ghi789',
tarball: 'https://registry.example.com/foo/-/foo-3.0.0.tgz',
},
},
},
time: {
'2.0.0': '2025-02-01T00:00:00.000Z',
'3.0.0': '2025-03-01T00:00:00.000Z',
},
}
expect(() => {
failIfTrustDowngraded(meta, '3.0.0', createPackageVersionPolicy(['foo@3.0.0']))
}).not.toThrow()
expect(() => {
failIfTrustDowngraded(meta, '3.0.0')
}).toThrow('High-risk trust downgrade')
})
test('allows downgrade when package name is in exclude list (all versions)', () => {
const meta: PackageMetaWithTime = {
name: 'bar',
'dist-tags': { latest: '3.0.0' },
versions: {
'2.0.0': {
name: 'bar',
version: '2.0.0',
_npmUser: {
name: 'test-publisher',
email: 'publisher@example.com',
trustedPublisher: {
id: 'test-provider',
oidcConfigId: 'oidc:test-config-123',
},
},
dist: {
shasum: 'def456',
tarball: 'https://registry.example.com/bar/-/bar-2.0.0.tgz',
},
},
'3.0.0': {
name: 'bar',
version: '3.0.0',
dist: {
shasum: 'ghi789',
tarball: 'https://registry.example.com/bar/-/bar-3.0.0.tgz',
},
},
},
time: {
'2.0.0': '2025-02-01T00:00:00.000Z',
'3.0.0': '2025-03-01T00:00:00.000Z',
},
}
expect(() => {
failIfTrustDowngraded(meta, '3.0.0', createPackageVersionPolicy(['bar']))
}).not.toThrow()
})
})

View File

@@ -15,6 +15,9 @@
{
"path": "../../config/pick-registry-for-package"
},
{
"path": "../../config/version-policy"
},
{
"path": "../../crypto/hash"
},

View File

@@ -109,6 +109,7 @@ export interface PreferredVersions {
export interface ResolveOptions {
alwaysTryWorkspacePackages?: boolean
trustPolicy?: TrustPolicy
trustPolicyExclude?: PackageVersionPolicy
defaultTag?: string
pickLowestVersion?: boolean
publishedBy?: Date

View File

@@ -138,6 +138,7 @@ export interface RequestPackageOptions {
calcSpecifier?: boolean
pinnedVersion?: PinnedVersion
trustPolicy?: TrustPolicy
trustPolicyExclude?: PackageVersionPolicy
}
export type BundledManifestFunction = () => Promise<BundledManifest | undefined>