"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;
}
