@salesforce/plugin-release-management
Version:
A plugin for preparing and publishing npm packages
123 lines • 4.98 kB
JavaScript
;
/*
* Copyright (c) 2020, salesforce.com, inc.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.signVerifyUpload = exports.getSfdxProperty = exports.SECURITY_PATH = exports.BASE_URL = void 0;
/**
* Signed npm packages have two fields that point plugin-trust to the URLs for the public key and signature file
* see src/package.ts/#PackageJsonSfdxProperty
*
* This code generates a keypair using node's crypto library,
* signs the tarball,
* verifies the signature
* and uploads the .sig and .crt (public key) to their AWS bucket
*
* The private key is never persisted to disk and ephemeral (exists only during this signing process)
* There are no security issues returning the sig/pubKey contents because those will be public
*
* This verification uses the sig/crt in memory--it verifies the match before uploading. Other code verifies it from S3.
*
* For security reasons, the url paths and bucket are hardcoded.
*/
const crypto_1 = require("crypto");
const fs_1 = require("fs");
const upload_1 = require("../codeSigning/upload");
const CRYPTO_LEVEL = 'RSA-SHA256';
const BUCKET = 'dfc-data-production';
exports.BASE_URL = 'https://developer.salesforce.com';
exports.SECURITY_PATH = 'media/salesforce-cli/security';
const getSfdxProperty = (packageName, packageVersion) => {
const fullPathNoExtension = `${exports.BASE_URL}/${exports.SECURITY_PATH}/${packageName}/${packageVersion}`;
return {
publicKeyUrl: `${fullPathNoExtension}.crt`,
signatureUrl: `${fullPathNoExtension}.sig`,
};
};
exports.getSfdxProperty = getSfdxProperty;
const signVerifyUpload = async (signingRequest) => {
const { publicKey, privateKey } = await getOneTimeUseKeys();
const { packageName, packageVersion } = signingRequest;
const signatureContents = await getSignature(privateKey, signingRequest.targetFileToSign);
// verify that signature/key/data worked properly
await verify(signingRequest.targetFileToSign, publicKey, signatureContents);
const output = {
publicKeyContents: publicKey,
signatureContents,
packageJsonSfdxProperty: (0, exports.getSfdxProperty)(packageName, packageVersion),
fileTarPath: signingRequest.targetFileToSign,
packageName,
packageVersion,
};
if (signingRequest.upload) {
await upload(output);
}
return output;
};
exports.signVerifyUpload = signVerifyUpload;
/**
* Save the security items (publicKey and .sig file) to AWS based on the generates filenames
*/
const upload = async (input) => {
return Promise.all([
// signature file
(0, upload_1.putObject)(BUCKET, input.packageJsonSfdxProperty.signatureUrl.replace(`${exports.BASE_URL}/`, ''), input.signatureContents),
// publicKey
(0, upload_1.putObject)(BUCKET, input.packageJsonSfdxProperty.publicKeyUrl.replace(`${exports.BASE_URL}/`, ''), input.publicKeyContents),
]);
};
const getOneTimeUseKeys = () => {
return new Promise((resolve, reject) => {
(0, crypto_1.generateKeyPair)('rsa', {
modulusLength: 4096,
publicKeyEncoding: {
type: 'spki',
format: 'pem',
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem',
},
}, (err, publicKey, privateKey) => {
if (err)
reject(err);
resolve({
publicKey,
privateKey,
});
});
});
};
const getSignature = async (privateKey, dataToSignFilePath) => {
const signApi = (0, crypto_1.createSign)(CRYPTO_LEVEL);
return new Promise((resolve, reject) => {
const dataToSignStream = (0, fs_1.createReadStream)(dataToSignFilePath, { encoding: 'binary' });
dataToSignStream.pipe(signApi);
dataToSignStream.on('end', () => {
return resolve(signApi.sign(privateKey, 'base64'));
});
dataToSignStream.on('error', (err) => {
return reject(err);
});
});
};
const verify = async (dataToSignFilePath, publicKey, signature) => {
return new Promise((resolve, reject) => {
const verifier = (0, crypto_1.createVerify)(CRYPTO_LEVEL);
const dataToVerifyStream = (0, fs_1.createReadStream)(dataToSignFilePath, { encoding: 'binary' });
dataToVerifyStream.pipe(verifier);
dataToVerifyStream.on('end', () => {
if (verifier.verify(publicKey, signature, 'base64')) {
return resolve(true);
}
return reject('The signature did not verify');
});
dataToVerifyStream.on('error', (err) => {
return reject(err);
});
});
};
//# sourceMappingURL=SimplifiedSigning.js.map