safelaunch 1.0.31 → 1.0.32
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/package.json +1 -1
- package/src/scan.js +64 -6
package/package.json
CHANGED
package/src/scan.js
CHANGED
|
@@ -73,6 +73,16 @@ const IMPACTS = {
|
|
|
73
73
|
impact: "Known exploits exist for these packages. Shipping them puts your users and infrastructure at risk.",
|
|
74
74
|
fix: "Run npm audit fix or check npm audit for manual fixes.",
|
|
75
75
|
}),
|
|
76
|
+
PYTHON_MISMATCH: (expected, actual) => ({
|
|
77
|
+
title: `Python version mismatch (expected: ${expected}, running: ${actual})`,
|
|
78
|
+
impact: "Code that works locally may fail in CI or production if they use the expected version.",
|
|
79
|
+
fix: `Switch to Python ${expected} or update .python-version to match your runtime.`,
|
|
80
|
+
}),
|
|
81
|
+
NODE_ENGINES_MISMATCH: (expected, actual) => ({
|
|
82
|
+
title: `Node version mismatch (package.json engines: ${expected}, running: ${actual})`,
|
|
83
|
+
impact: "Your package.json declares a required Node version that differs from what is running.",
|
|
84
|
+
fix: `Switch to Node ${expected} or update the engines field in package.json.`,
|
|
85
|
+
}),
|
|
76
86
|
NODE_MISMATCH: (expected, actual) => ({
|
|
77
87
|
title: `Node version mismatch (.nvmrc: ${expected}, running: ${actual})`,
|
|
78
88
|
impact: "Code that works locally may fail in CI or production if they use the expected version.",
|
|
@@ -216,16 +226,63 @@ function checkNpmAudit(cwd) {
|
|
|
216
226
|
|
|
217
227
|
function checkNodeVersion(cwd) {
|
|
218
228
|
const issues = [];
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const
|
|
223
|
-
if (
|
|
224
|
-
|
|
229
|
+
const actual = process.version.replace(/^v/, "");
|
|
230
|
+
|
|
231
|
+
// Check .nvmrc
|
|
232
|
+
const nvmrc = readFileSafe(path.join(cwd, ".nvmrc"));
|
|
233
|
+
if (nvmrc) {
|
|
234
|
+
const expected = nvmrc.trim().replace(/^v/, "");
|
|
235
|
+
if (expected.split(".")[0] !== actual.split(".")[0]) {
|
|
236
|
+
issues.push({ severity: "warn", ...IMPACTS.NODE_MISMATCH(expected, actual) });
|
|
237
|
+
}
|
|
225
238
|
}
|
|
239
|
+
|
|
240
|
+
// Check package.json engines.node
|
|
241
|
+
const pkgRaw = readFileSafe(path.join(cwd, "package.json"));
|
|
242
|
+
if (pkgRaw) {
|
|
243
|
+
try {
|
|
244
|
+
const pkg = JSON.parse(pkgRaw);
|
|
245
|
+
const enginesNode = pkg.engines && pkg.engines.node;
|
|
246
|
+
if (enginesNode) {
|
|
247
|
+
const match = enginesNode.match(/(\d+)/);
|
|
248
|
+
if (match && match[1] !== actual.split(".")[0]) {
|
|
249
|
+
issues.push({ severity: "warn", ...IMPACTS.NODE_ENGINES_MISMATCH(enginesNode, actual) });
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
} catch {}
|
|
253
|
+
}
|
|
254
|
+
|
|
226
255
|
return issues;
|
|
227
256
|
}
|
|
228
257
|
|
|
258
|
+
function checkPythonVersion(cwd) {
|
|
259
|
+
const issues = [];
|
|
260
|
+
const actual = exec("python3 --version") || exec("python --version");
|
|
261
|
+
if (!actual) return issues;
|
|
262
|
+
const actualVer = actual.replace(/^Python\s+/i, "").trim();
|
|
263
|
+
|
|
264
|
+
// Check .python-version
|
|
265
|
+
const pyVer = readFileSafe(path.join(cwd, ".python-version"));
|
|
266
|
+
if (pyVer) {
|
|
267
|
+
const expected = pyVer.trim();
|
|
268
|
+
if (expected.split(".")[0] !== actualVer.split(".")[0]) {
|
|
269
|
+
issues.push({ severity: "warn", ...IMPACTS.PYTHON_MISMATCH(expected, actualVer) });
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Check runtime.txt (Heroku/Render style)
|
|
274
|
+
const runtimeTxt = readFileSafe(path.join(cwd, "runtime.txt"));
|
|
275
|
+
if (runtimeTxt && runtimeTxt.startsWith("python-")) {
|
|
276
|
+
const expected = runtimeTxt.replace("python-", "").trim();
|
|
277
|
+
if (expected.split(".")[0] !== actualVer.split(".")[0]) {
|
|
278
|
+
issues.push({ severity: "warn", ...IMPACTS.PYTHON_MISMATCH(expected, actualVer) });
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return issues;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
|
|
229
286
|
function renderHookOutput(blockers, warnings, infos, elapsed) {
|
|
230
287
|
const lines = [];
|
|
231
288
|
const hr = gray("─".repeat(49));
|
|
@@ -325,6 +382,7 @@ async function runScan(options = {}) {
|
|
|
325
382
|
...checkTypeScript(cwd),
|
|
326
383
|
...checkNpmAudit(cwd),
|
|
327
384
|
...checkNodeVersion(cwd),
|
|
385
|
+
...checkPythonVersion(cwd),
|
|
328
386
|
];
|
|
329
387
|
const blockers = allIssues.filter((i) => i.severity === "block");
|
|
330
388
|
const warnings = allIssues.filter((i) => i.severity === "warn");
|