@atomist/sdm
Version:
Atomist Software Delivery Machine SDK
101 lines • 4.58 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.wrapEventHandlersToVerifySignature = exports.EventSigningAutomationEventListener = void 0;
const metadataReading_1 = require("@atomist/automation-client/lib/internal/metadata/metadataReading");
const constructionUtils_1 = require("@atomist/automation-client/lib/util/constructionUtils");
const logger_1 = require("@atomist/automation-client/lib/util/logger");
const crypto = require("crypto");
const _ = require("lodash");
const array_1 = require("../util/misc/array");
/**
* AutomationEventListener that signs outgoing custom events with a configurable
* JWS signature key.
*/
class EventSigningAutomationEventListener {
constructor(esc) {
this.esc = esc;
this.initVerificationKeys();
}
async onMutation(options) {
if (eventMatch(options.name, this.esc.events)) {
const privateKey = crypto.createPrivateKey({
key: this.esc.signingKey.privateKey,
passphrase: this.esc.signingKey.passphrase,
});
const { default: CompactSign } = require("jose/jws/compact/sign");
for (const key of Object.getOwnPropertyNames(options.variables || {})) {
const value = options.variables[key];
const jws = await new CompactSign(Buffer.from(JSON.stringify(value)))
.setProtectedHeader({ alg: "ES512" })
.sign(privateKey);
value.signature = jws;
logger_1.logger.debug(`Signed custom event '${options.name}'`);
}
}
return options;
}
initVerificationKeys() {
this.esc.verificationKeys = array_1.toArray(this.esc.verificationKeys) || [];
// If signing key is set, also use it to verify
if (!!this.esc.signingKey) {
this.esc.verificationKeys.push(this.esc.signingKey);
}
}
}
exports.EventSigningAutomationEventListener = EventSigningAutomationEventListener;
/**
* Wrap every event handler that is registered and its subscription name matches a configurable set of
* regular expression patterns for event signature verification.
*/
function wrapEventHandlersToVerifySignature(handlers, options) {
const wh = [];
for (const handler of handlers) {
const instance = constructionUtils_1.toFactory(handler)();
const md = metadataReading_1.metadataFromInstance(instance);
if (eventMatch(md.subscriptionName, options.events)) {
wh.push(() => (Object.assign(Object.assign({}, md), { handle: async (e, ctx, params) => {
const { default: compactVerify } = require("jose/jws/compact/verify");
for (const key of Object.getOwnPropertyNames(e.data)) {
const evv = e.data[key][0];
if (!evv.signature) {
throw new Error("Signature missing on incoming event");
}
let verified = false;
for (const pkey of array_1.toArray(options.verificationKeys)) {
const publicKey = crypto.createPublicKey({
key: pkey.publicKey,
});
try {
const { payload } = await compactVerify(evv.signature, publicKey);
e.data[key][0] = _.merge({}, evv, JSON.parse(Buffer.from(payload).toString()));
verified = true;
logger_1.logger.debug(`Verified signature on custom event '${md.subscriptionName}'`);
break;
}
catch (e) {
// return undefined;
}
}
if (!verified) {
throw new Error("Signature verification failed for incoming event");
}
}
return instance.handle(e, ctx, params);
} })));
}
else {
wh.push(handler);
}
}
return wh;
}
exports.wrapEventHandlersToVerifySignature = wrapEventHandlersToVerifySignature;
function eventMatch(event, patterns) {
for (const pattern of patterns) {
if (new RegExp(pattern).test(event)) {
return true;
}
}
return false;
}
//# sourceMappingURL=eventSigning.js.map