From 93c1fb0b76a9f8d99b1a8807e08442577cb62b64 Mon Sep 17 00:00:00 2001 From: Arda Cinar Date: Fri, 13 Jan 2023 20:45:14 +0300 Subject: [PATCH] LibGemini: Disallow cases forbidden in the spec The gemini spec at does not allow status lines and meta messages to start with a byte order mark. It also forbids meta messages longer than 1024 bytes. --- Userland/Libraries/LibGemini/Job.cpp | 29 ++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/Userland/Libraries/LibGemini/Job.cpp b/Userland/Libraries/LibGemini/Job.cpp index a6883afdab..83ab0aad74 100644 --- a/Userland/Libraries/LibGemini/Job.cpp +++ b/Userland/Libraries/LibGemini/Job.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -127,6 +128,8 @@ void Job::on_socket_connected() if (m_state == State::Failed) return; + // https://gemini.circumlunar.space/docs/specification.gmi + if (m_state == State::InStatus) { if (!can_read_line()) return; @@ -141,6 +144,19 @@ void Job::on_socket_connected() auto line = line_or_error.release_value(); auto view = line.bytes_as_string_view(); + auto first_code_point = line.code_points().begin().peek(); + if (!first_code_point.has_value()) { + dbgln("Job: empty status line"); + m_state = State::Failed; + return deferred_invoke([this] { did_fail(Core::NetworkJob::Error::ProtocolFailed); }); + } + + if (first_code_point.release_value() == 0xFEFF) { + dbgln("Job: Byte order mark as first character of status line"); + m_state = State::Failed; + return deferred_invoke([this] { did_fail(Core::NetworkJob::Error::ProtocolFailed); }); + } + auto maybe_space_index = view.find(' '); if (!maybe_space_index.has_value()) { dbgln("Job: Expected 2-part status line, got '{}'", line); @@ -159,6 +175,19 @@ void Job::on_socket_connected() return deferred_invoke([this] { did_fail(Core::NetworkJob::Error::ProtocolFailed); }); } + auto meta_first_code_point = Utf8View(second_part).begin().peek(); + if (meta_first_code_point.release_value() == 0xFEFF) { + dbgln("Job: Byte order mark as first character of meta"); + m_state = State::Failed; + return deferred_invoke([this] { did_fail(Core::NetworkJob::Error::ProtocolFailed); }); + } + + if (second_part.length() > 1024) { + dbgln("Job: Meta too long"); + m_state = State::Failed; + return deferred_invoke([this] { did_fail(Core::NetworkJob::Error::ProtocolFailed); }); + } + m_status = status.release_value(); m_meta = second_part;