-
Igor Markin authoredIgor Markin authored
MimeVerificationService.js 6.55 KiB
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const normalizationStrategies_1 = require("./normalizationStrategies");
const MailParser_1 = require("../MailParser/MailParser");
const CryptoService_1 = require("../CryptoService");
const StatusesService_1 = require("../StatusesService");
const index_1 = require("./index");
const rka_1 = require("../../utils/rka");
const common_1 = require("../../utils/common");
const VerificationError_1 = require("../VerificationService/VerificationError");
const stringUtils_1 = require("../../utils/stringUtils");
const utils_1 = require("./utils");
const vendorAmendingFunctions = {
[StatusesService_1.EMAIL_VENDORS.GMAIL]: normalizationStrategies_1.amendGmailNodes,
[StatusesService_1.EMAIL_VENDORS.OUTLOOK]: normalizationStrategies_1.amendOutlookNodes,
};
const vendorPruningFunctions = {
[StatusesService_1.EMAIL_VENDORS.GMAIL]: normalizationStrategies_1.pruneGmailElement,
[StatusesService_1.EMAIL_VENDORS.OUTLOOK]: normalizationStrategies_1.pruneOutlookElement,
};
const vendorAttributesCleanupFunctions = {
[StatusesService_1.EMAIL_VENDORS.GMAIL]: normalizationStrategies_1.cleanupGMailElementAttributes,
[StatusesService_1.EMAIL_VENDORS.OUTLOOK]: normalizationStrategies_1.cleanupOutlookElementAttributes,
};
const vendorPrintingFunctions = {
[StatusesService_1.EMAIL_VENDORS.OUTLOOK]: utils_1.printOutlookElement,
};
// Load JSDOM dynamically for Node env only, because built CRA is crashing with it
let JSDOM;
const loadJSDOM = () => __awaiter(void 0, void 0, void 0, function* () {
if (!JSDOM) {
JSDOM = (yield Promise.resolve().then(() => require("jsdom"))).JSDOM;
}
});
typeof window === "undefined" && loadJSDOM();
const prepareMimeForPartsVerification = (mimeString, attachmentsHashes, senderSystem, sourceHtmlRabinFingerprint, sourceHtmlSize, sourcePlainRabinFingerprint, sourcePlainSize) => __awaiter(void 0, void 0, void 0, function* () {
const { htmlPart, plainPart } = getNormalizedMimeParts(mimeString, senderSystem);
const originalHtml = findSubstring(htmlPart, sourceHtmlRabinFingerprint, sourceHtmlSize);
if (originalHtml === null) {
throw new VerificationError_1.default("Unable to verify MIME. Source HTML part was not found.");
}
const originalPlain = findSubstring(plainPart, sourcePlainRabinFingerprint, sourcePlainSize);
if (originalPlain === null) {
throw new VerificationError_1.default("Unable to verify MIME. Source Plain part was not found.");
}
return yield index_1.default.preparePartsForSigning([originalHtml, originalPlain], attachmentsHashes);
});
const getNormalizedMimeParts = (mimeString, senderSystem) => {
const { htmlPart, plainPart, } = index_1.default.getMimeHtmlAndPlainParts(mimeString);
const normalizedHtmlPart = index_1.default.normalizeVendorHtml(htmlPart, senderSystem);
const normalizedPlainPart = stringUtils_1.removeSpacesAndLinebreaks(plainPart);
return {
htmlPart: normalizedHtmlPart,
plainPart: normalizedPlainPart,
};
};
const findSubstring = (string, rabinFingerprint, substringSize) => {
if (!string.length) {
return "";
}
const startingIndex = rka_1.default.searchFingerprintInText(string, rabinFingerprint, substringSize);
if (startingIndex === -1) {
return null;
}
return string.slice(startingIndex, startingIndex + substringSize);
};
const getMimeHtmlAndPlainParts = (mimeString) => {
mimeString = MailParser_1.fixNewLines(mimeString);
const mimeParts = MailParser_1.default.parseMIME(mimeString);
// TODO: check, whether we need to support 2+ html/plain MIME parts.
const htmlPart = MailParser_1.default.getHTML(mimeString, mimeParts);
const plainPart = MailParser_1.default.getPlain(mimeString, mimeParts);
return {
htmlPart,
plainPart,
};
};
/**
* @param parts - html and plain parts strings
* @param attachmentsHashes - base64 strings of the attachments hashes
*/
const preparePartsForSigning = (parts, attachmentsHashes) => __awaiter(void 0, void 0, void 0, function* () {
let hashedParts = yield Promise.all(parts.map((part) => __awaiter(void 0, void 0, void 0, function* () {
return common_1.arrayBufferToBase64(yield CryptoService_1.default.SHA256(part));
})));
hashedParts = [...hashedParts, ...attachmentsHashes];
return hashedParts.sort().join("\n");
});
const normalizeVendorHtml = (htmlString, vendor) => {
let document;
if (typeof DOMParser !== "undefined") {
const parser = new DOMParser();
document = parser.parseFromString(htmlString, "text/html");
}
else {
const { window } = new JSDOM(htmlString);
document = window.document;
}
const mimeBody = document.body;
const amendNodesFunction = vendorAmendingFunctions[vendor];
if (amendNodesFunction) {
amendNodesFunction(document);
}
/**
* Remove unnecessary nodes
*/
const elementPruningFunction = vendorPruningFunctions[vendor];
if (!elementPruningFunction) {
throw new Error(`Vendor "${vendor}" is not supported. Please, develop a pruning function for it.`);
}
utils_1.pruneHtmlNode(document, elementPruningFunction);
/**
* Cleanup unnecessary attributes of nodes
*/
const elementAttributesCleanupFunction = vendorAttributesCleanupFunctions[vendor];
if (elementAttributesCleanupFunction) {
utils_1.cleanupHtmlNodeAttributes(document, elementAttributesCleanupFunction);
}
/**
* Print nodes
*/
const vendorPrintFunction = vendorPrintingFunctions[vendor];
return utils_1.printHtmlChildren(mimeBody, vendorPrintFunction, 0);
};
const normalizePlainPart = (text) => {
return stringUtils_1.removeSpacesAndLinebreaks(text);
};
exports.default = {
normalizeVendorHtml,
getMimeHtmlAndPlainParts,
preparePartsForSigning,
prepareMimeForPartsVerification,
getNormalizedMimeParts,
normalizePlainPart,
};