@theoptimalpartner/npm-security-kit 2.0.0-beta.5 → 2.0.0-beta.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -49,6 +49,8 @@ Sale primero como beta (`2.0.0-beta.N`), con el dist-tag `beta`.
49
49
  (`npmSecurityKit.nodeUpgradeDeferredUntil` y `nodeUpgradeReason` en el `package.json`, hasta 183 días): mientras
50
50
  está vigente, los errores de la línea de Node y de npm 10 son avisos e `init` no toca `engines`. El kit la sugiere
51
51
  cuando el salto es grande.
52
+ - **2.0.0-beta.6:** `init` señala un solo step para el kit: el primero de los PR (si hay varios con `npm ci`, los
53
+ otros no lo necesitan), o el primero del pipeline si no hay `pull-requests`, con la sugerencia de correrlo en los PR.
52
54
 
53
55
  ### BREAKING
54
56
 
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  Reglas de seguridad de dependencias npm en un solo comando que corre en el pipeline **antes de `npm ci`**. No tiene
4
4
  dependencias: `npx` descarga un solo tarball y revisa el lockfile antes de que se ejecute un solo install script.
5
5
 
6
- Los ejemplos usan la versión de este paquete (`2.0.0-beta.5`). Las publicadas, con sus dist-tags:
6
+ Los ejemplos usan la versión de este paquete (`2.0.0-beta.6`). Las publicadas, con sus dist-tags:
7
7
  `npm view @theoptimalpartner/npm-security-kit dist-tags`.
8
8
 
9
9
  ## Qué revisa
@@ -69,7 +69,7 @@ definitions:
69
69
  - npm
70
70
  script:
71
71
  # Antes de instalar: malware, vulnerabilidades, Node, .npmrc, install scripts, Dockerfiles
72
- - npx --yes --min-release-age=0 @theoptimalpartner/npm-security-kit@2.0.0-beta.5 check
72
+ - npx --yes --min-release-age=0 @theoptimalpartner/npm-security-kit@2.0.0-beta.6 check
73
73
  - npm ci # con strict-allow-scripts, los scripts no aprobados cortan acá
74
74
  - npm audit signatures
75
75
  # … y lo que ya corría el step: build, lint, tests
@@ -89,13 +89,13 @@ pipelines:
89
89
 
90
90
  - **Antes de `npm ci`:** `npm audit` funciona solo con el lockfile, así que el malware se detecta antes de que
91
91
  corra su `postinstall`.
92
- - **Versión fija** (`@2.0.0-beta.5`): subir la política en un repo es un cambio visible en su PR.
92
+ - **Versión fija** (`@2.0.0-beta.6`): subir la política en un repo es un cambio visible en su PR.
93
93
  - **`--min-release-age=0` solo para `npx`:** `npx` no aplica `min-release-age-exclude` (npm 11.19.1 lo aplica al
94
94
  instalar, no al resolver el paquete de `npx`). Sin el flag, dentro de un repo con `min-release-age=7` una versión
95
95
  nueva del kit no se puede usar hasta cumplir 7 días (`ETARGET … with a date before`). Afecta solo al kit: la
96
96
  versión va fija y no tiene dependencias. El `npm ci` que sigue aplica los 7 días como siempre.
97
97
  - **`npm audit signatures`** va después de `npm ci` porque necesita `node_modules`.
98
- - Para guardar el resultado como artefacto: `npx --yes --min-release-age=0 @theoptimalpartner/npm-security-kit@2.0.0-beta.5 check --json > npm-security-kit.json`.
98
+ - Para guardar el resultado como artefacto: `npx --yes --min-release-age=0 @theoptimalpartner/npm-security-kit@2.0.0-beta.6 check --json > npm-security-kit.json`.
99
99
 
100
100
  ### Dockerfile
101
101
 
@@ -119,11 +119,11 @@ marca como error.
119
119
  Una vez por repo, en una rama:
120
120
 
121
121
  ```bash
122
- npx --yes --min-release-age=0 @theoptimalpartner/npm-security-kit@2.0.0-beta.5 init --dry-run
122
+ npx --yes --min-release-age=0 @theoptimalpartner/npm-security-kit@2.0.0-beta.6 init --dry-run
123
123
  ```
124
124
 
125
125
  ```bash
126
- npx --yes --min-release-age=0 @theoptimalpartner/npm-security-kit@2.0.0-beta.5 init
126
+ npx --yes --min-release-age=0 @theoptimalpartner/npm-security-kit@2.0.0-beta.6 init
127
127
  ```
128
128
 
129
129
  `init`:
@@ -142,7 +142,7 @@ Después: revisar el diff, aprobar o negar los install scripts, aplicar el step
142
142
  ### En local
143
143
 
144
144
  ```bash
145
- npx --yes --min-release-age=0 @theoptimalpartner/npm-security-kit@2.0.0-beta.5 check
145
+ npx --yes --min-release-age=0 @theoptimalpartner/npm-security-kit@2.0.0-beta.6 check
146
146
  ```
147
147
 
148
148
  No se instala como devDependency ni usa husky: el `.npmrc` protege el `npm install` local y el pipeline es el
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theoptimalpartner/npm-security-kit",
3
- "version": "2.0.0-beta.5",
3
+ "version": "2.0.0-beta.6",
4
4
  "description": "Revisa el lockfile antes de npm ci: malware, vulnerabilidades, versión de Node, .npmrc, install scripts y Dockerfiles",
5
5
  "type": "module",
6
6
  "bin": {
package/src/init.js CHANGED
@@ -129,7 +129,8 @@ function lineProblem(cal, major, policy) {
129
129
  }
130
130
 
131
131
  // Las líneas `- npm ci` del pipeline: las del step que corre en los PR (inline o por ancla) y el resto
132
- // (deploys, publicación). El kit va en el de los PR. Sin sección pull-requests, todas cuentan como PR.
132
+ // (deploys, publicación). El kit va en el de los PR. Sin sección pull-requests (o sin npm ci en ella),
133
+ // `noPr`: el kit va en el primer step que corre npm ci, y con uno alcanza.
133
134
  export function npmCiLines(text) {
134
135
  const lines = (text ?? '').split(/\r?\n/);
135
136
  const indent = (l) => l.match(/^ */)[0].length;
@@ -145,14 +146,15 @@ export function npmCiLines(text) {
145
146
  return out;
146
147
  };
147
148
  const prStart = lines.findIndex((l) => /^\s+pull-requests:\s*(#.*)?$/.test(l));
148
- if (prStart < 0) return { pr: all, others: [] };
149
+ const first = { pr: all.slice(0, 1), others: all.slice(1), noPr: true };
150
+ if (prStart < 0) return first;
149
151
  const prBlock = blockAfter(prStart);
150
152
  const pr = new Set(prBlock.filter((i) => isCi(lines[i])).map((i) => i + 1));
151
153
  for (const anchor of new Set(prBlock.flatMap((i) => [...lines[i].matchAll(/\*([\w-]+)/g)].map((m) => m[1])))) {
152
154
  const def = lines.findIndex((l) => new RegExp(`&${anchor}(\\s|$)`).test(l));
153
155
  if (def >= 0) for (const i of blockAfter(def)) if (isCi(lines[i])) pr.add(i + 1);
154
156
  }
155
- if (!pr.size) return { pr: all, others: [] };
157
+ if (!pr.size) return first;
156
158
  return { pr: [...pr].sort((a, b) => a - b), others: all.filter((n) => !pr.has(n)) };
157
159
  }
158
160
 
@@ -345,13 +347,25 @@ export async function runInit(ctx, { dryRun = false, stdout = process.stdout, co
345
347
  if (used) {
346
348
  say(` Ya corre el kit (${used[0]}).${used[1] !== ctx.kitVersion ? ` Para esta versión: ${PACKAGE}@${ctx.kitVersion}.` : ''}`);
347
349
  } else if (ci.pr.length) {
348
- say(` Agregar al step que corre en los PR (bitbucket-pipelines.yml:${ci.pr.join(', ')}), antes de su npm ci, sin reemplazar el resto del step:`);
350
+ say(
351
+ ci.noPr
352
+ ? ` El pipeline no corre nada en los PR (no hay \`pull-requests\`): agregar el kit en el primer step que corre npm ci (bitbucket-pipelines.yml:${ci.pr[0]}), antes de su npm ci, sin reemplazar el resto del step:`
353
+ : ` Agregar al step que corre en los PR (bitbucket-pipelines.yml:${ci.pr[0]}), antes de su npm ci, sin reemplazar el resto del step:`,
354
+ );
349
355
  say();
350
356
  say(` - npx --yes --min-release-age=0 ${PACKAGE}@${ctx.kitVersion} check # antes de npm ci`);
351
357
  say(' - npm audit signatures # después de npm ci');
352
358
  say();
359
+ const lines = (list) => `${list.length === 1 ? 'línea' : 'líneas'} ${list.join(', ')}`;
360
+ if (!ci.noPr && ci.pr.length > 1) {
361
+ say(` Los otros steps de los PR que corren npm ci (${lines(ci.pr.slice(1))}) no lo necesitan: con uno por pipeline alcanza.`);
362
+ }
353
363
  if (ci.others.length) {
354
- say(` Los otros npm ci (líneas ${ci.others.join(', ')}: deploys, publicación) no necesitan el kit: ya pasó en el PR.`);
364
+ say(
365
+ ci.noPr
366
+ ? ` Los otros npm ci (${lines(ci.others)}) no necesitan el kit: con uno por pipeline alcanza.`
367
+ : ` Los otros npm ci (${lines(ci.others)}: deploys, publicación) no necesitan el kit: ya pasó en el PR.`,
368
+ );
355
369
  }
356
370
  if (!deferral && !new RegExp(`node:${image.replace(/\./g, '\\.')}(\\s|$|-)`).test(pipeline)) {
357
371
  say(` Con una imagen que traiga npm >= ${ctx.policy.minNpmVersion} (image: node:${image}).`);
@@ -359,7 +373,12 @@ export async function runInit(ctx, { dryRun = false, stdout = process.stdout, co
359
373
  if (!/^\s+ci:\s*(#.*)?$/m.test(pipeline)) {
360
374
  say(' Para correrlo a mano ("Run pipeline"), un `custom: ci` con el mismo step (ver el README).');
361
375
  }
362
- if (!/condition:[\s\S]*BITBUCKET_PR_DESTINATION_BRANCH/.test(pipeline)) {
376
+ if (ci.noPr) {
377
+ say(
378
+ ` Conviene que ese step corra en los PR a las ramas que despliegan${branches.length ? ` (${branches.join(', ')})` : ''}: ` +
379
+ 'una sección `pull-requests` con el step y su condition, como en el ejemplo del README.',
380
+ );
381
+ } else if (!/condition:[\s\S]*BITBUCKET_PR_DESTINATION_BRANCH/.test(pipeline)) {
363
382
  say(` Y correrlo solo en los PR a las ramas que despliegan${branches.length ? ` (${branches.join(', ')})` : ''}, con la condition del step:`);
364
383
  say();
365
384
  say(' condition:');