UNPKG

@salesforce/plugin-trust

Version:

validate a digital signature for a npm package

102 lines 3.75 kB
import { Lifecycle } from '@salesforce/core'; import { NpmModule } from '../shared/npmCommand.js'; export const hook = (options) => Promise.all([registryCheck(options)]); /** * Validates that a string is a well-formed HTTP/HTTPS URL * * @param urlString - The URL string to validate * @returns true if valid, false otherwise */ const isValidRegistryUrl = (urlString) => { try { const url = new URL(urlString); // Only allow http/https protocols to prevent protocol-based attacks return url.protocol === 'http:' || url.protocol === 'https:'; } catch { return false; } }; /** * Sanitizes a registry URL to prevent command injection * Validates URL format and ensures no shell metacharacters * * @param registryUrl - The registry URL to sanitize * @returns The sanitized URL or undefined if invalid */ const sanitizeRegistryUrl = (registryUrl) => { if (!registryUrl || typeof registryUrl !== 'string') { return undefined; } // Trim whitespace const trimmed = registryUrl.trim(); // Check for shell metacharacters that could enable command injection const dangerousChars = /[;&|`$(){}[\]<>\\'"]/; if (dangerousChars.test(trimmed)) { return undefined; } // Validate as proper URL if (!isValidRegistryUrl(trimmed)) { return undefined; } return trimmed; }; const registryCheck = async (options) => { const pluginName = '@salesforce/plugin-trust'; // find npm install const npm = new NpmModule(''); const env = process.env.npm_config_registry ?? process.env.NPM_CONFIG_REGISTRY; let sanitizedEnv; if (env) { sanitizedEnv = sanitizeRegistryUrl(env); if (sanitizedEnv) { options.doctor.addSuggestion(`using npm registry ${sanitizedEnv} from environment variable`); } else { options.doctor.addSuggestion(`WARNING: npm registry environment variable contains invalid or potentially unsafe URL: ${env}`); } } const config = npm.run('config get registry').stdout.trim(); let sanitizedConfig; if (config) { sanitizedConfig = sanitizeRegistryUrl(config); if (sanitizedConfig) { options.doctor.addSuggestion(`using npm registry ${sanitizedConfig} from npm config`); } else { options.doctor.addSuggestion(`WARNING: npm config registry contains invalid or potentially unsafe URL: ${config}`); } } await Promise.all([ ...new Set([ // npm and yarn registries 'https://registry.npmjs.org', 'https://registry.yarnpkg.com', sanitizedEnv ?? sanitizedConfig ?? '', ]), ] // incase customRegistry is undefined, prevent printing an extra line .filter((u) => u) .map(async (url) => { try { const results = npm.ping(url); // timeout after 5000ms, error if (!results || results.time > 5000) { // to trigger the catch/fail below throw Error; } await Lifecycle.getInstance().emit('Doctor:diagnostic', { testName: `[${pluginName}] can ping: ${url}`, status: 'pass', }); } catch (e) { await Lifecycle.getInstance().emit('Doctor:diagnostic', { testName: `[${pluginName}] can't ping: ${url}`, status: 'fail', }); options.doctor.addSuggestion(`Cannot ping ${url} - potential network configuration error, check proxies, firewalls, environment variables. Verify this by running 'npm ping ${url}'`); } })); }; //# sourceMappingURL=diagnostics.js.map