// Resolve the effective allowScripts policy from the layered sources. // Returns `{ policy, source }` where: // - `policy` is an object map of `package-spec` -> boolean, or `null` if // no layer has any configuration // - `source` is one of `'cli'`, `'package.json'`, `'.npmrc'`, or `null` // // Precedence order (highest to lowest), per RFC npm/rfcs#868: // 1. CLI flags (--allow-scripts) and env vars // 2. Root `package.json#allowScripts` // 3. `.npmrc` cascade (project, user, global) // // The project `package.json` layer is skipped when: // - `npm.global` is true (no project context exists for global installs) // - `skipProjectConfig` is true (e.g. npm exec / npx, which per the RFC // consult only user/global .npmrc) // // In both skipped cases, the CLI and .npmrc layers are still consulted; // only the project package.json layer is skipped. // // The first source with any configuration wins for the entire install; // lower layers are ignored. A `log.warn` is emitted whenever a setting is // being suppressed by a higher-priority source. // // Reads `package.json` from `npm.prefix` (not `npm.localPrefix`) so an // install run from a workspace sub-directory still picks up the project // root's policy. const resolveAllowScripts = async (npm, { skipProjectConfig = false } = {}) => { // Independently probe each RFC layer. const cliPolicy = policyFromSources(npm, ['cli', 'env']) const npmrcPolicy = policyFromSources(npm, ['project', 'user', 'global', 'builtin']) // The --allow-scripts CLI flag is intended for one-off and global // contexts (npm exec, npx, npm install -g). In a project-scoped install, // team policy belongs in package.json or .npmrc, so reject the flag // outright to avoid the "works on my machine" footgun. if (cliPolicy && !npm.global && !skipProjectConfig) { throw Object.assign( new Error( '--allow-scripts is not allowed in project-scoped installs. ' + 'Add the entries to the "allowScripts" field in package.json, ' + 'or to .npmrc, instead.' ), { code: 'EALLOWSCRIPTS' } ) } // Project package.json is consulted only when the caller is operating // inside a real project (not -g, not npx). let pkgPolicy = null if (!npm.global && !skipProjectConfig) { try { const { content } = await pkgJson.normalize(npm.prefix) if (content?.allowScripts && typeof content.allowScripts === 'object') { const entries = Object.entries(content.allowScripts) if (entries.length > 0) { pkgPolicy = Object.fromEntries(entries) } } } catch (err) { log.silly('install-scripts', 'no package.json at prefix', err.message)