"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.detectVulnerabilityAlerts = void 0;
const tslib_1 = require("tslib");
const error_messages_1 = require("../../../constants/error-messages");
const logger_1 = require("../../../logger");
const crate_1 = require("../../../modules/datasource/crate");
const github_tags_1 = require("../../../modules/datasource/github-tags");
const go_1 = require("../../../modules/datasource/go");
const maven_1 = require("../../../modules/datasource/maven");
const npm_1 = require("../../../modules/datasource/npm");
const nuget_1 = require("../../../modules/datasource/nuget");
const packagist_1 = require("../../../modules/datasource/packagist");
const pypi_1 = require("../../../modules/datasource/pypi");
const rubygems_1 = require("../../../modules/datasource/rubygems");
const platform_1 = require("../../../modules/platform");
const allVersioning = tslib_1.__importStar(require("../../../modules/versioning"));
const composerVersioning = tslib_1.__importStar(require("../../../modules/versioning/composer"));
const mavenVersioning = tslib_1.__importStar(require("../../../modules/versioning/maven"));
const npmVersioning = tslib_1.__importStar(require("../../../modules/versioning/npm"));
const pep440Versioning = tslib_1.__importStar(require("../../../modules/versioning/pep440"));
const rubyVersioning = tslib_1.__importStar(require("../../../modules/versioning/ruby"));
const semverVersioning = tslib_1.__importStar(require("../../../modules/versioning/semver"));
const markdown_1 = require("../../../util/markdown");
const regex_1 = require("../../../util/regex");
// TODO can return `null` and `undefined` (#7154)
async function detectVulnerabilityAlerts(input) {
    var _a, _b, _c;
    if (!input?.vulnerabilityAlerts) {
        return input;
    }
    if (input.vulnerabilityAlerts.enabled === false) {
        logger_1.logger.debug('Vulnerability alerts are disabled');
        return input;
    }
    const alerts = await platform_1.platform.getVulnerabilityAlerts();
    if (!alerts.length) {
        logger_1.logger.debug('No vulnerability alerts found');
        if (input.vulnerabilityAlertsOnly) {
            throw new Error(error_messages_1.NO_VULNERABILITY_ALERTS);
        }
        return input;
    }
    const config = { ...input };
    const versionings = {
        'github-tags': semverVersioning.id,
        go: semverVersioning.id,
        packagist: composerVersioning.id,
        maven: mavenVersioning.id,
        npm: npmVersioning.id,
        nuget: semverVersioning.id,
        pypi: pep440Versioning.id,
        rubygems: rubyVersioning.id,
    };
    const combinedAlerts = {};
    for (const alert of alerts) {
        if (alert.securityVulnerability?.package?.name === 'yargs-parser' &&
            (alert.vulnerableRequirements === '= 5.0.0-security.0' ||
                alert.vulnerableRequirements === '= 5.0.1')) {
            continue;
        }
        try {
            if (alert.dismissReason) {
                continue;
            }
            if (!alert.securityVulnerability.firstPatchedVersion) {
                logger_1.logger.debug({ alert }, 'Vulnerability alert has no firstPatchedVersion - skipping');
                continue;
            }
            const datasourceMapping = {
                ACTIONS: github_tags_1.GithubTagsDatasource.id,
                COMPOSER: packagist_1.PackagistDatasource.id,
                GO: go_1.GoDatasource.id,
                MAVEN: maven_1.MavenDatasource.id,
                NPM: npm_1.NpmDatasource.id,
                NUGET: nuget_1.NugetDatasource.id,
                PIP: pypi_1.PypiDatasource.id,
                RUBYGEMS: rubygems_1.RubyGemsDatasource.id,
                RUST: crate_1.CrateDatasource.id,
            };
            const datasource = datasourceMapping[alert.securityVulnerability.package.ecosystem];
            const depName = alert.securityVulnerability.package.name;
            const fileName = alert.vulnerableManifestPath;
            const fileType = alert.vulnerableManifestFilename;
            const firstPatchedVersion = alert.securityVulnerability.firstPatchedVersion.identifier;
            const advisory = alert.securityAdvisory;
            // TODO #7154
            let vulnerableRequirements = alert.vulnerableRequirements;
            // istanbul ignore if
            if (!vulnerableRequirements.length) {
                if (datasource === maven_1.MavenDatasource.id) {
                    vulnerableRequirements = `(,${firstPatchedVersion})`;
                }
                else {
                    vulnerableRequirements = `< ${firstPatchedVersion}`;
                }
            }
            if (datasource === pypi_1.PypiDatasource.id) {
                vulnerableRequirements = vulnerableRequirements.replace((0, regex_1.regEx)(/^= /), '== ');
            }
            if (datasource === github_tags_1.GithubTagsDatasource.id) {
                // GitHub Actions uses docker versioning, which doesn't support `= 1.2.3` matching, so we strip the equals
                vulnerableRequirements = vulnerableRequirements.replace(/^=\s*/, '');
            }
            combinedAlerts[fileName] || (combinedAlerts[fileName] = {});
            (_a = combinedAlerts[fileName])[datasource] || (_a[datasource] = {});
            (_b = combinedAlerts[fileName][datasource])[depName] || (_b[depName] = {});
            (_c = combinedAlerts[fileName][datasource][depName])[vulnerableRequirements] || (_c[vulnerableRequirements] = {
                advisories: [],
            });
            const alertDetails = combinedAlerts[fileName][datasource][depName][vulnerableRequirements];
            alertDetails.advisories.push(advisory);
            const version = allVersioning.get(versionings[datasource]);
            if (version.isVersion(firstPatchedVersion)) {
                if (!alertDetails.firstPatchedVersion ||
                    version.isGreaterThan(firstPatchedVersion, alertDetails.firstPatchedVersion)) {
                    alertDetails.firstPatchedVersion = firstPatchedVersion;
                }
            }
            else {
                logger_1.logger.debug('Invalid firstPatchedVersion: ' + firstPatchedVersion);
            }
            alertDetails.fileType = fileType;
        }
        catch (err) {
            logger_1.logger.warn({ err }, 'Error parsing vulnerability alert');
        }
    }
    const alertPackageRules = [];
    config.remediations = {};
    for (const [fileName, files] of Object.entries(combinedAlerts)) {
        for (const [datasource, dependencies] of Object.entries(files)) {
            for (const [depName, currentValues] of Object.entries(dependencies)) {
                for (const [matchCurrentVersion, val] of Object.entries(currentValues)) {
                    let prBodyNotes = [];
                    try {
                        prBodyNotes = ['### GitHub Vulnerability Alerts'].concat(val.advisories.map((advisory) => {
                            const identifiers = advisory.identifiers;
                            const description = advisory.description;
                            let content = '#### ';
                            let heading;
                            if (identifiers.some((id) => id.type === 'CVE')) {
                                heading = identifiers
                                    .filter((id) => id.type === 'CVE')
                                    .map((id) => id.value)
                                    .join(' / ');
                            }
                            else {
                                heading = identifiers.map((id) => id.value).join(' / ');
                            }
                            if (advisory.references.length) {
                                heading = `[${heading}](${advisory.references[0].url})`;
                            }
                            content += heading;
                            content += '\n\n';
                            content += (0, markdown_1.sanitizeMarkdown)(description);
                            return content;
                        }));
                    }
                    catch (err) /* istanbul ignore next */ {
                        logger_1.logger.warn({ err }, 'Error generating vulnerability PR notes');
                    }
                    // TODO: types (#7154)
                    const allowedVersions = datasource === pypi_1.PypiDatasource.id
                        ? `==${val.firstPatchedVersion}`
                        : val.firstPatchedVersion;
                    const matchFiles = datasource === go_1.GoDatasource.id
                        ? [fileName.replace('go.sum', 'go.mod')]
                        : [fileName];
                    let matchRule = {
                        matchDatasources: [datasource],
                        matchPackageNames: [depName],
                        matchCurrentVersion,
                        matchFiles,
                    };
                    const supportedRemediationFileTypes = ['package-lock.json'];
                    if (config.transitiveRemediation &&
                        supportedRemediationFileTypes.includes(val.fileType)) {
                        const remediations = config.remediations;
                        remediations[fileName] ?? (remediations[fileName] = []);
                        const currentVersion = matchCurrentVersion.replace('=', '').trim();
                        const newVersion = allowedVersions;
                        const remediation = {
                            datasource,
                            depName,
                            currentVersion,
                            newVersion,
                            prBodyNotes,
                        };
                        remediations[fileName].push(remediation);
                    }
                    else {
                        // Remediate only direct dependencies
                        matchRule = {
                            ...matchRule,
                            allowedVersions,
                            prBodyNotes,
                            isVulnerabilityAlert: true,
                            force: {
                                ...config.vulnerabilityAlerts,
                            },
                        };
                        // istanbul ignore if
                        if (config.transitiveRemediation &&
                            matchRule.matchFiles?.[0] === 'package.json') {
                            matchRule.force.rangeStrategy = 'replace';
                        }
                    }
                    alertPackageRules.push(matchRule);
                }
            }
        }
    }
    logger_1.logger.debug({ alertPackageRules }, 'alert package rules');
    config.packageRules = (config.packageRules ?? []).concat(alertPackageRules);
    return config;
}
exports.detectVulnerabilityAlerts = detectVulnerabilityAlerts;
//# sourceMappingURL=vulnerability.js.map