fix(mime-parsing): ensure that text content for multipart nodes always ends with a newline. Fixes #571

This commit is contained in:
Andris Reinman 2023-12-07 13:53:27 +02:00
parent e553523df3
commit 6f4994d3a0
No known key found for this signature in database
GPG key ID: DC6C83F4D584D364
6 changed files with 91 additions and 0 deletions

View file

@ -58,4 +58,5 @@ module.exports = function (grunt) {
// Tasks
grunt.registerTask('default', ['eslint', 'shell:server', 'wait:server', 'mochaTest', 'shell:server:kill']);
grunt.registerTask('testonly', ['shell:server', 'wait:server', 'mochaTest', 'shell:server:kill']);
grunt.registerTask('proto', ['mochaTest:imap']);
};

View file

@ -125,6 +125,23 @@ class MIMEParser {
node.message = parse(node.body.join(''));
}
if (node.body && node.body.length && node.multipart) {
// find last character from an array of strings
let lastChar;
let lastIndex = 0;
for (let i = node.body.length - 1; i >= 0; i--) {
if (typeof node.body[i] === 'string' && node.body[i].length) {
lastChar = node.body[i].at(-1);
lastIndex = i;
break;
}
}
if (lastChar && lastChar !== '\n') {
// ensure that non-multipart body text always ends with a newline
node.body[lastIndex] += '\n';
}
}
node.lineCount = node.body.length ? node.body.length - 1 : 0;
node.body = Buffer.from(
node.body

View file

@ -0,0 +1,22 @@
Content-Type: multipart/mixed; boundary="------------cWFvDSey27tFG0hVYLqp9hs9"
MIME-Version: 1.0
Message-ID: <beep-boop@example-1.com>
To: foo-1@example-1.com
From: foo-1@example-1.com
Subject: test
This is a multi-part message in MIME format.
--------------cWFvDSey27tFG0hVYLqp9hs9
Content-Type: text/plain; charset=UTF-8; format=flowed
Content-Transfer-Encoding: 7bit
test
--------------cWFvDSey27tFG0hVYLqp9hs9
Content-Type: text/plain; charset=UTF-8; name="example.txt"
Content-Disposition: attachment; filename="example.txt"
Content-Transfer-Encoding: base64
ZXhhbXBsZQo=
--------------cWFvDSey27tFG0hVYLqp9hs9--

View file

@ -0,0 +1,22 @@
Content-Type: multipart/mixed; boundary="------------cWFvDSey27tFG0hVYLqp9hs9"
MIME-Version: 1.0
Message-ID: <beep-boop@example-1.com>
To: foo-1@example-1.com
From: foo-1@example-1.com
Subject: test
This is a multi-part message in MIME format.
--------------cWFvDSey27tFG0hVYLqp9hs9
Content-Type: text/plain; charset=UTF-8; format=flowed
Content-Transfer-Encoding: 7bit
test
--------------cWFvDSey27tFG0hVYLqp9hs9
Content-Type: text/plain; charset=UTF-8; name="example.txt"
Content-Disposition: attachment; filename="example.txt"
Content-Transfer-Encoding: base64
ZXhhbXBsZQo=
--------------cWFvDSey27tFG0hVYLqp9hs9--

View file

@ -3,11 +3,20 @@
'use strict';
const MIMEParser = require('../lib/indexer/parse-mime-tree').MIMEParser;
const Indexer = require('../lib/indexer/indexer');
const indexer = new Indexer();
const fs = require('fs');
const chai = require('chai');
const expect = chai.expect;
chai.config.includeStack = true;
const fixtures = {
no_empty_line_between_text_boundary: {
eml: fs.readFileSync(__dirname + '/fixtures/no_empty_line_between_text_boundary.eml')
}
};
describe('#parseValueParams', function () {
it('should return continuation value as mime-word', function () {
let parser = new MIMEParser();
@ -44,4 +53,23 @@ describe('#parseValueParams', function () {
hasParams: true
});
});
it('should parse a file with no empty line between text and boundary', function (done) {
// parse a file and then make sure that boundary is correct
let source = Buffer.concat([fixtures.no_empty_line_between_text_boundary.eml]);
let parser = new MIMEParser(source);
parser.parse();
parser.finalizeTree();
let parsed = parser.tree.childNodes[0];
indexer.bodyQuery(parsed, '', (err, data) => {
expect(err).to.not.exist;
expect(data.toString().indexOf('This is a multi-part message in MIME format.\r\n--------------cWFvDSey27tFG0hVYLqp9hs9')).to.gt(0);
done();
});
});
});

View file

@ -5,6 +5,7 @@
"main": "server.js",
"scripts": {
"test": "mongo --eval 'db.dropDatabase()' wildduck-test && redis-cli -n 13 flushdb && npm run runtest",
"test:proto": "NODE_ENV=test grunt proto",
"printconf": "NODE_CONFIG_ONLY=true npm start",
"runtest": "NODE_ENV=test grunt",
"runci": "npm run printconf && npm run runtest",