({ arb, npm, idealTreeOpts })
| 14 | // walks it for nodes whose install scripts have not been covered by |
| 15 | // the `allowScripts` policy, and throws if any are found. |
| 16 | const strictAllowScriptsPreflight = async ({ arb, npm, idealTreeOpts }) => { |
| 17 | if (!npm?.flatOptions?.strictAllowScripts) { |
| 18 | return |
| 19 | } |
| 20 | |
| 21 | // Prefer the idealTree when reify is about to run; fall back to |
| 22 | // actualTree for npm rebuild (which never builds an ideal tree). |
| 23 | let tree |
| 24 | if (idealTreeOpts !== undefined) { |
| 25 | // `npm ci` builds the ideal tree before calling the preflight, so |
| 26 | // skip the rebuild when one already exists. `npm install` calls the |
| 27 | // preflight before reify and needs us to build. |
| 28 | if (!arb.idealTree) { |
| 29 | await arb.buildIdealTree(idealTreeOpts) |
| 30 | } |
| 31 | tree = arb.idealTree |
| 32 | } else { |
| 33 | tree = arb.actualTree |
| 34 | } |
| 35 | |
| 36 | const unreviewed = await checkAllowScripts({ arb, npm, tree }) |
| 37 | if (unreviewed.length === 0) { |
| 38 | return |
| 39 | } |
| 40 | |
| 41 | const lines = unreviewed.map(({ node, scripts }) => { |
| 42 | const events = Object.entries(scripts) |
| 43 | .map(([event, body]) => `${event}: ${body}`) |
| 44 | .join('; ') |
| 45 | const name = node.package?.name || node.name |
| 46 | const version = node.package?.version || '' |
| 47 | const label = version ? `${name}@${version}` : name |
| 48 | return ` ${label} (${events})` |
| 49 | }).join('\n') |
| 50 | |
| 51 | // `npm install-scripts` writes to a project package.json, which doesn't |
| 52 | // exist for global installs. Point global users at the `--allow-scripts` |
| 53 | // flag and `npm config set allow-scripts`, which both work for global |
| 54 | // installs. Use the trusted display identity so the suggested `npm config |
| 55 | // set` value matches what the policy matches on, not the tarball's |
| 56 | // self-reported name. |
| 57 | const names = unreviewed.map(({ node }) => trustedDisplay(node).name) |
| 58 | const remediation = npm.global |
| 59 | ? 'Allow them with `--allow-scripts`, persist them with ' + |
| 60 | `\`${configSetAllowScripts(names)}\`, or bypass this ` + |
| 61 | 'check with `--dangerously-allow-all-scripts`.' |
| 62 | : 'Approve them with `npm install-scripts approve`, deny them with ' + |
| 63 | '`npm install-scripts deny`, or bypass this check with ' + |
| 64 | '`--dangerously-allow-all-scripts`.' |
| 65 | |
| 66 | throw Object.assign( |
| 67 | new Error( |
| 68 | `--strict-allow-scripts: ${unreviewed.length} package(s) have install ` + |
| 69 | `scripts not covered by allowScripts:\n${lines}\n` + |
| 70 | remediation |
| 71 | ), |
| 72 | { code: 'ESTRICTALLOWSCRIPTS' } |
| 73 | ) |
no test coverage detected