UNPKG

nx

Version:

The core Nx plugin contains the core functionality of Nx like the project graph, nx commands and task orchestration.

106 lines (105 loc) 5.79 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ProvenanceError = void 0; exports.ensurePackageHasProvenance = ensurePackageHasProvenance; exports.getNxPackageGroup = getNxPackageGroup; const child_process_1 = require("child_process"); const path_1 = require("path"); const fileutils_1 = require("./fileutils"); const package_manager_1 = require("./package-manager"); /* * Verifies that the given npm package has provenance attestations * generated by the GitHub Actions workflow at .github/workflows/publish.yml * in the nrwl/nx repository. * * Will throw if the package does not have valid provenance. */ async function ensurePackageHasProvenance(packageName, packageVersion) { // this is used for locally released versions without provenance // do not set this for other reasons or you might be exposed to security risks if (process.env.NX_SKIP_PROVENANCE_CHECK) { return; } try { const result = await (0, package_manager_1.packageRegistryView)(packageName, packageVersion, '--json --silent'); const npmViewResult = JSON.parse(result); const attURL = npmViewResult.dist?.attestations?.url; if (!attURL) throw new ProvenanceError(packageName, packageVersion, 'No attestation URL found'); const response = await fetch(attURL); if (!response.ok) { throw new ProvenanceError(packageName, packageVersion, `HTTP ${response.status}: ${response.statusText}`); } const attestations = (await response.json()); const provenanceAttestation = attestations?.attestations?.find((a) => a.predicateType === 'https://slsa.dev/provenance/v1'); const dsseEnvelopePayload = JSON.parse(Buffer.from(provenanceAttestation.bundle.dsseEnvelope.payload, 'base64').toString()); const workflowParameters = dsseEnvelopePayload?.predicate?.buildDefinition?.externalParameters ?.workflow; // verify that provenance was actually generated from the right publishing workflow if (!workflowParameters) { throw new ProvenanceError(packageName, packageVersion, 'Missing workflow parameters in attestation'); } if (workflowParameters.repository !== 'https://github.com/nrwl/nx') { throw new ProvenanceError(packageName, packageVersion, 'Repository does not match nrwl/nx'); } if (workflowParameters.path !== '.github/workflows/publish.yml') { throw new ProvenanceError(packageName, packageVersion, 'Publishing workflow does not match .github/workflows/publish.yml'); } if (workflowParameters.ref !== `refs/tags/${npmViewResult.version}`) { throw new ProvenanceError(packageName, packageVersion, `Version ref does not match refs/tags/${npmViewResult.version}`); } // verify that provenance was generated from the exact same artifact as the one we are installing const distSha = Buffer.from(npmViewResult.dist.integrity.replace('sha512-', ''), 'base64').toString('hex'); const attestationSha = dsseEnvelopePayload.subject[0].digest.sha512; if (distSha !== attestationSha) { throw new ProvenanceError(packageName, packageVersion, 'Integrity hash does not match attestation hash'); } return; } catch (error) { if (error instanceof ProvenanceError) { throw error; } throw new ProvenanceError(packageName, packageVersion, error.message || error); } } class ProvenanceError extends Error { constructor(packageName, packageVersion, error) { let customRegistry = undefined; try { const packageManager = (0, package_manager_1.detectPackageManager)(); const commands = (0, package_manager_1.getPackageManagerCommand)(packageManager); // Try to get registry from current package manager, fall back to npm const registryCommand = commands.getRegistryUrl ?? 'npm config get registry'; const registry = (0, child_process_1.execSync)(registryCommand, { timeout: 5000, windowsHide: true, encoding: 'utf-8', }).trim(); // Only consider it custom if it's not the default npm registry if (registry && registry !== 'undefined' && !registry.includes('registry.npmjs.org')) { customRegistry = registry; } } catch { // If we can't determine the registry, proceed with default error message } const registryNote = customRegistry ? `This might be due to a custom registry configuration (${customRegistry}). Please check whether provenance is correctly configured for your registry.` : `This could indicate a security risk. Please double check https://www.npmjs.com/package/${packageName} to see if the package is published correctly or file an issue at https://github.com/nrwl/nx/issues.`; super(`An error occurred while checking the provenance of ${packageName}@${packageVersion}. ${registryNote} To disable this check at your own risk, you can set the NX_SKIP_PROVENANCE_CHECK environment variable to true. \n Error: ${error ?? ''}`); } } exports.ProvenanceError = ProvenanceError; function getNxPackageGroup() { const packageJsonPath = (0, path_1.join)(__dirname, '../../package.json'); const packageJson = (0, fileutils_1.readJsonFile)(packageJsonPath); if (!packageJson['nx-migrations']?.packageGroup) { return ['nx']; } const packages = packageJson['nx-migrations'].packageGroup.filter((dep) => typeof dep === 'string' && dep.startsWith('@nx/')); packages.push('nx'); return packages; }