From 8a4f759a78c8d608e9d61f4c8a55eab6e7124f28 Mon Sep 17 00:00:00 2001 From: Reinier Criel Date: Fri, 27 Mar 2026 14:25:58 -0700 Subject: [PATCH] Some cleanup --- .../interceptors/npm/modifyNpmInfo.js | 12 +-- .../interceptors/npm/npmInterceptor.js | 83 +++++++++++++++---- 2 files changed, 67 insertions(+), 28 deletions(-) diff --git a/packages/safe-chain/src/registryProxy/interceptors/npm/modifyNpmInfo.js b/packages/safe-chain/src/registryProxy/interceptors/npm/modifyNpmInfo.js index dfab97b..d8468d6 100644 --- a/packages/safe-chain/src/registryProxy/interceptors/npm/modifyNpmInfo.js +++ b/packages/safe-chain/src/registryProxy/interceptors/npm/modifyNpmInfo.js @@ -1,4 +1,4 @@ -import { getMinimumPackageAgeHours, getNpmMinimumPackageAgeExclusions } from "../../../config/settings.js"; +import { getMinimumPackageAgeHours } from "../../../config/settings.js"; import { ui } from "../../../environment/userInteraction.js"; import { getHeaderValueAsString } from "../../http-utils.js"; @@ -65,16 +65,6 @@ export function modifyNpmInfoResponse(body, headers) { return body; } - // Check if this package is excluded from minimum age filtering - const packageName = bodyJson.name; - const exclusions = getNpmMinimumPackageAgeExclusions(); - if (packageName && exclusions.some((pattern) => matchesExclusionPattern(packageName, pattern))) { - ui.writeVerbose( - `Safe-chain: ${packageName} is excluded from minimum package age filtering (minimumPackageAgeExclusions setting).` - ); - return body; - } - const cutOff = new Date( new Date().getTime() - getMinimumPackageAgeHours() * 3600 * 1000 ); diff --git a/packages/safe-chain/src/registryProxy/interceptors/npm/npmInterceptor.js b/packages/safe-chain/src/registryProxy/interceptors/npm/npmInterceptor.js index c1310bd..8a6d7eb 100644 --- a/packages/safe-chain/src/registryProxy/interceptors/npm/npmInterceptor.js +++ b/packages/safe-chain/src/registryProxy/interceptors/npm/npmInterceptor.js @@ -46,37 +46,86 @@ function buildNpmInterceptor(registry) { reqContext.targetUrl, registry ); + const minimumAgeChecksEnabled = !skipMinimumPackageAge(); + const packageIsExcludedFromMinimumAgeChecks = + packageName && isExcludedFromMinimumPackageAge(packageName); if (await isMalwarePackage(packageName, version)) { reqContext.blockMalware(packageName, version); return; } - if (!skipMinimumPackageAge() && isPackageInfoUrl(reqContext.targetUrl)) { + if (minimumAgeChecksEnabled && isPackageInfoUrl(reqContext.targetUrl)) { reqContext.modifyRequestHeaders(modifyNpmInfoRequestHeaders); - reqContext.modifyBody(modifyNpmInfoResponse); + reqContext.modifyBody((body, headers) => { + const metadataPackageName = getPackageNameFromMetadataResponse( + body, + headers + ); + + if ( + metadataPackageName && + isExcludedFromMinimumPackageAge(metadataPackageName) + ) { + return body; + } + + return modifyNpmInfoResponse(body, headers); + }); return; } // For tarball requests the metadata check above is skipped, so we check the // new packages list as a fallback (covers e.g. frozen-lockfile installs). - if (!skipMinimumPackageAge() && packageName && version) { - const exclusions = getNpmMinimumPackageAgeExclusions(); - const isExcluded = exclusions.some((pattern) => - matchesExclusionPattern(packageName, pattern) - ); + if ( + minimumAgeChecksEnabled && + packageName && + version && + !packageIsExcludedFromMinimumAgeChecks + ) { + const newPackagesDatabase = await openNewPackagesDatabase(); - if (!isExcluded) { - const newPackagesDatabase = await openNewPackagesDatabase(); - - if (newPackagesDatabase.isNewlyReleasedPackage(packageName, version)) { - reqContext.blockMinimumAgeRequest( - packageName, - version, - `Forbidden - blocked by safe-chain direct download minimum package age (${packageName}@${version})` - ); - } + if (newPackagesDatabase.isNewlyReleasedPackage(packageName, version)) { + reqContext.blockMinimumAgeRequest( + packageName, + version, + `Forbidden - blocked by safe-chain direct download minimum package age (${packageName}@${version})` + ); } } }); } + +/** + * @param {string} packageName + * @returns {boolean} + */ +function isExcludedFromMinimumPackageAge(packageName) { + const exclusions = getNpmMinimumPackageAgeExclusions(); + return exclusions.some((pattern) => + matchesExclusionPattern(packageName, pattern) + ); +} + +/** + * @param {Buffer} body + * @param {NodeJS.Dict | undefined} headers + * @returns {string | undefined} + */ +function getPackageNameFromMetadataResponse(body, headers) { + try { + const contentType = headers?.["content-type"]; + const normalizedContentType = Array.isArray(contentType) + ? contentType.join(",") + : contentType; + + if (!normalizedContentType?.toLowerCase().includes("application/json")) { + return undefined; + } + + const bodyJson = JSON.parse(body.toString("utf8")); + return typeof bodyJson.name === "string" ? bodyJson.name : undefined; + } catch { + return undefined; + } +}