message/mime-object: cap references
Cap the references, since the threading algo is recursive.
This commit is contained in:
@ -26,6 +26,7 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
#include <unordered_set>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
@ -476,16 +477,16 @@ MimeMessage::contacts(Contact::Type ctype) const noexcept
|
|||||||
* message-ids (in that order). Duplicates are removed.
|
* message-ids (in that order). Duplicates are removed.
|
||||||
*
|
*
|
||||||
* The _first_ one in the list determines the thread-id for the message.
|
* The _first_ one in the list determines the thread-id for the message.
|
||||||
|
*
|
||||||
|
* The number of references is capped (max_references), since the thread algo is
|
||||||
|
* recursive, and should still be plenty big.
|
||||||
*/
|
*/
|
||||||
std::vector<std::string>
|
std::vector<std::string>
|
||||||
MimeMessage::references() const noexcept
|
MimeMessage::references() const noexcept
|
||||||
{
|
{
|
||||||
// is ref already in the list? O(n) but with small n.
|
constexpr size_t max_references = 256;
|
||||||
auto is_dup = [](auto&& seq, const std::string& ref) {
|
|
||||||
return std::ranges::any_of(seq, [&](auto&& str) { return ref == str; });
|
|
||||||
};
|
|
||||||
|
|
||||||
auto on_blacklist = [](auto&& msgid) {
|
const auto on_blacklist = [](const char* msgid) {
|
||||||
// don't include empty message-ids
|
// don't include empty message-ids
|
||||||
if (!*msgid)
|
if (!*msgid)
|
||||||
return true;
|
return true;
|
||||||
@ -498,6 +499,7 @@ MimeMessage::references() const noexcept
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::vector<std::string> refs;
|
std::vector<std::string> refs;
|
||||||
|
std::unordered_set<std::string> seen; // O(1) dup-check, keeps this O(n)
|
||||||
for (auto&& ref_header: { "References", "In-reply-to" }) {
|
for (auto&& ref_header: { "References", "In-reply-to" }) {
|
||||||
|
|
||||||
auto hdr{header(ref_header)};
|
auto hdr{header(ref_header)};
|
||||||
@ -508,16 +510,28 @@ MimeMessage::references() const noexcept
|
|||||||
if (!mime_refs)
|
if (!mime_refs)
|
||||||
continue; /* try the next header */
|
continue; /* try the next header */
|
||||||
|
|
||||||
refs.reserve(refs.size() + g_mime_references_length(mime_refs));
|
const auto n_refs{g_mime_references_length(mime_refs)};
|
||||||
|
refs.reserve(refs.size() + n_refs);
|
||||||
|
|
||||||
for (auto i = 0; i != g_mime_references_length(mime_refs); ++i) {
|
for (auto i = 0; i != n_refs; ++i) {
|
||||||
const auto msgid{g_mime_references_get_message_id(mime_refs, i)};
|
const auto msgid{g_mime_references_get_message_id(mime_refs, i)};
|
||||||
if (msgid && !is_dup(refs, msgid) && !on_blacklist(msgid))
|
if (msgid && !on_blacklist(msgid) && seen.emplace(msgid).second)
|
||||||
refs.emplace_back(msgid);
|
refs.emplace_back(msgid);
|
||||||
}
|
}
|
||||||
g_mime_references_free(mime_refs);
|
g_mime_references_free(mime_refs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cap hostile/pathological headers: keep the first reference (thread-id)
|
||||||
|
// plus the most-recent ancestors (the tail).
|
||||||
|
if (refs.size() > max_references) {
|
||||||
|
std::vector<std::string> capped;
|
||||||
|
capped.reserve(max_references);
|
||||||
|
capped.emplace_back(std::move(refs.front()));
|
||||||
|
for (auto it = refs.end() - (max_references - 1); it != refs.end(); ++it)
|
||||||
|
capped.emplace_back(std::move(*it));
|
||||||
|
refs = std::move(capped);
|
||||||
|
}
|
||||||
|
|
||||||
return refs;
|
return refs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -866,6 +866,44 @@ On Thu, Aug 04, 2022 at 05:31:39PM +0100, Robin Murphy wrote:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_message_references_capped()
|
||||||
|
{
|
||||||
|
// a too-big References header must not produce an
|
||||||
|
// unbounded reference list (see MimeMessage::references): keep
|
||||||
|
// first (thread-id) + most-recent ancestors.
|
||||||
|
constexpr size_t n_refs = 1000;
|
||||||
|
constexpr size_t cap = 256; // must match max_references
|
||||||
|
|
||||||
|
std::string hdr{"References:"};
|
||||||
|
for (size_t i = 0; i != n_refs; ++i)
|
||||||
|
hdr += mu_format(" <r{:04}@x>", i);
|
||||||
|
|
||||||
|
const auto msgtext =
|
||||||
|
hdr + "\n" +
|
||||||
|
"To: \"Robin Murphy\" <robin.murphy@arm.com>\n"
|
||||||
|
"From: \"Dan Carpenter\" <dan.carpenter@oracle.com>\n"
|
||||||
|
"Subject: capped\n"
|
||||||
|
"Date: Fri, 5 Aug 2022 09:37:02 +0300\n"
|
||||||
|
"Message-Id: <capped@kadam>\n"
|
||||||
|
"\n"
|
||||||
|
"body\n";
|
||||||
|
|
||||||
|
auto message{Message::make_from_text(
|
||||||
|
msgtext,
|
||||||
|
"/home/test/Maildir/inbox/cur/162342449279256.88888_1.evergrey:2,S")};
|
||||||
|
g_assert_true(!!message);
|
||||||
|
|
||||||
|
const auto refs{message->references()};
|
||||||
|
g_assert_cmpuint(refs.size(), ==, cap);
|
||||||
|
// thread-id (first) preserved, and the immediate parent (last) kept.
|
||||||
|
assert_equal(refs.front(), "r0000@x");
|
||||||
|
assert_equal(refs.back(), mu_format("r{:04}@x", n_refs - 1));
|
||||||
|
// the middle is dropped: second entry is the start of the kept tail.
|
||||||
|
assert_equal(refs.at(1), mu_format("r{:04}@x", n_refs - (cap - 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
test_message_outlook_body()
|
test_message_outlook_body()
|
||||||
{
|
{
|
||||||
@ -1109,6 +1147,8 @@ main(int argc, char* argv[])
|
|||||||
test_message_calendar);
|
test_message_calendar);
|
||||||
g_test_add_func("/message/message/references",
|
g_test_add_func("/message/message/references",
|
||||||
test_message_references);
|
test_message_references);
|
||||||
|
g_test_add_func("/message/message/references-capped",
|
||||||
|
test_message_references_capped);
|
||||||
g_test_add_func("/message/message/outlook-body",
|
g_test_add_func("/message/message/outlook-body",
|
||||||
test_message_outlook_body);
|
test_message_outlook_body);
|
||||||
g_test_add_func("/message/message/message-id",
|
g_test_add_func("/message/message/message-id",
|
||||||
|
|||||||
Reference in New Issue
Block a user