"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.tryDecrypt = tryDecrypt;
exports.decryptConfig = decryptConfig;
const tslib_1 = require("tslib");
const is_1 = tslib_1.__importDefault(require("@sindresorhus/is"));
const logger_1 = require("../logger");
const regex_1 = require("../util/regex");
const sanitize_1 = require("../util/sanitize");
const url_1 = require("../util/url");
const kbpgp_1 = require("./decrypt/kbpgp");
const legacy_1 = require("./decrypt/legacy");
const openpgp_1 = require("./decrypt/openpgp");
const global_1 = require("./global");
const schema_1 = require("./schema");
async function tryDecrypt(privateKey, encryptedStr, repository, keyName) {
    let decryptedStr = null;
    if (privateKey?.startsWith('-----BEGIN PGP PRIVATE KEY BLOCK-----')) {
        const decryptedObjStr = process.env.RENOVATE_X_USE_OPENPGP === 'true'
            ? await (0, openpgp_1.tryDecryptOpenPgp)(privateKey, encryptedStr)
            : await (0, kbpgp_1.tryDecryptKbPgp)(privateKey, encryptedStr);
        if (decryptedObjStr) {
            decryptedStr = validateDecryptedValue(decryptedObjStr, repository);
        }
    }
    else {
        decryptedStr = (0, legacy_1.tryDecryptPublicKeyDefault)(privateKey, encryptedStr);
        if (is_1.default.string(decryptedStr)) {
            logger_1.logger.warn({ keyName }, 'Encrypted value is using deprecated default padding, please change to using PGP encryption.');
        }
        else {
            decryptedStr = (0, legacy_1.tryDecryptPublicKeyPKCS1)(privateKey, encryptedStr);
            // istanbul ignore if
            if (is_1.default.string(decryptedStr)) {
                logger_1.logger.warn({ keyName }, 'Encrypted value is using deprecated PKCS1 padding, please change to using PGP encryption.');
            }
        }
    }
    return decryptedStr;
}
function validateDecryptedValue(decryptedObjStr, repository) {
    try {
        const decryptedObj = schema_1.DecryptedObject.safeParse(decryptedObjStr);
        // istanbul ignore if
        if (!decryptedObj.success) {
            const error = new Error('config-validation');
            error.validationError = `Could not parse decrypted config.`;
            throw error;
        }
        const { o: org, r: repo, v: value } = decryptedObj.data;
        if (is_1.default.nonEmptyString(value)) {
            if (is_1.default.nonEmptyString(org)) {
                const orgPrefixes = org
                    .split(',')
                    .map((o) => o.trim())
                    .map((o) => o.toUpperCase())
                    .map((o) => (0, url_1.ensureTrailingSlash)(o));
                if (is_1.default.nonEmptyString(repo)) {
                    const scopedRepos = orgPrefixes.map((orgPrefix) => `${orgPrefix}${repo}`.toUpperCase());
                    if (scopedRepos.some((r) => r === repository.toUpperCase())) {
                        return value;
                    }
                    else {
                        logger_1.logger.debug({ scopedRepos }, 'Secret is scoped to a different repository');
                        const error = new Error('config-validation');
                        error.validationError = `Encrypted secret is scoped to a different repository: "${scopedRepos.join(',')}".`;
                        throw error;
                    }
                }
                else {
                    if (orgPrefixes.some((orgPrefix) => repository.toUpperCase().startsWith(orgPrefix))) {
                        return value;
                    }
                    else {
                        logger_1.logger.debug({ orgPrefixes }, 'Secret is scoped to a different org');
                        const error = new Error('config-validation');
                        error.validationError = `Encrypted secret is scoped to a different org: "${orgPrefixes.join(',')}".`;
                        throw error;
                    }
                }
            }
            else {
                const error = new Error('config-validation');
                error.validationError = `Encrypted value in config is missing a scope.`;
                throw error;
            }
        }
        else {
            const error = new Error('config-validation');
            error.validationError = `Encrypted value in config is missing a value.`;
            throw error;
        }
    }
    catch (err) {
        logger_1.logger.warn({ err }, 'Could not parse decrypted string');
    }
    return null;
}
async function decryptConfig(config, repository) {
    logger_1.logger.trace({ config }, 'decryptConfig()');
    const decryptedConfig = { ...config };
    const privateKey = global_1.GlobalConfig.get('privateKey');
    const privateKeyOld = global_1.GlobalConfig.get('privateKeyOld');
    for (const [key, val] of Object.entries(config)) {
        if (key === 'encrypted' && is_1.default.object(val)) {
            logger_1.logger.debug({ config: val }, 'Found encrypted config');
            const encryptedWarning = global_1.GlobalConfig.get('encryptedWarning');
            if (is_1.default.string(encryptedWarning)) {
                logger_1.logger.once.warn(encryptedWarning);
            }
            if (privateKey) {
                for (const [eKey, eVal] of Object.entries(val)) {
                    logger_1.logger.debug('Trying to decrypt ' + eKey);
                    let decryptedStr = await tryDecrypt(privateKey, eVal, repository, eKey);
                    if (privateKeyOld && !is_1.default.nonEmptyString(decryptedStr)) {
                        logger_1.logger.debug(`Trying to decrypt with old private key`);
                        decryptedStr = await tryDecrypt(privateKeyOld, eVal, repository, eKey);
                    }
                    if (!is_1.default.nonEmptyString(decryptedStr)) {
                        const error = new Error('config-validation');
                        error.validationError = `Failed to decrypt field ${eKey}. Please re-encrypt and try again.`;
                        throw error;
                    }
                    logger_1.logger.debug(`Decrypted ${eKey}`);
                    if (eKey === 'npmToken') {
                        const token = decryptedStr.replace((0, regex_1.regEx)(/\n$/), '');
                        decryptedConfig[eKey] = token;
                        (0, sanitize_1.addSecretForSanitizing)(token);
                    }
                    else {
                        decryptedConfig[eKey] = decryptedStr;
                        (0, sanitize_1.addSecretForSanitizing)(decryptedStr);
                    }
                }
            }
            else {
                logger_1.logger.error('Found encrypted data but no privateKey');
            }
            delete decryptedConfig.encrypted;
        }
        else if (is_1.default.array(val)) {
            decryptedConfig[key] = [];
            for (const item of val) {
                if (is_1.default.object(item) && !is_1.default.array(item)) {
                    decryptedConfig[key].push(await decryptConfig(item, repository));
                }
                else {
                    decryptedConfig[key].push(item);
                }
            }
        }
        else if (is_1.default.object(val) && key !== 'content') {
            decryptedConfig[key] = await decryptConfig(val, repository);
        }
    }
    delete decryptedConfig.encrypted;
    logger_1.logger.trace({ config: decryptedConfig }, 'decryptedConfig');
    return decryptedConfig;
}
//# sourceMappingURL=decrypt.js.map