@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 +2 -0
- package/README.md +7 -7
- package/package.json +1 -1
- package/src/init.js +25 -6
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
|
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(
|
|
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(
|
|
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 (
|
|
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:');
|