From 30df16b4eade9494c7849f406791aaba0b14132a Mon Sep 17 00:00:00 2001 From: Yaraslau Tamashevich Date: Wed, 23 Oct 2024 20:03:00 +0300 Subject: [PATCH] Request permission on big paste --- metainfo.xml | 1 + src/contour/TerminalSession.cpp | 52 +++++++++++++++++++++++-- src/contour/TerminalSession.h | 5 +++ src/contour/ui.template/Terminal.qml.in | 18 +++++++++ 4 files changed, 72 insertions(+), 4 deletions(-) diff --git a/metainfo.xml b/metainfo.xml index e5e25f5dc7..daf4a87fab 100644 --- a/metainfo.xml +++ b/metainfo.xml @@ -110,6 +110,7 @@
  • Expose current profile's name through env var `CONTOUR_PROFILE` (#1637)
  • Add terminal tabs (#90)
  • Add `SaveScreenshot` and `CopyScreenshot` action (#210)
  • +
  • Protect user from accidentally pasting too large input (#1198)
  • Add binding to exit normal mode with `Esc` (#1604)
  • Add config option to switch into insert mode after yank (#1604)
  • Improves window size/resize handling on HiDPI monitor settings (#1628)
  • diff --git a/src/contour/TerminalSession.cpp b/src/contour/TerminalSession.cpp index 860a789007..b42992d2fa 100644 --- a/src/contour/TerminalSession.cpp +++ b/src/contour/TerminalSession.cpp @@ -368,6 +368,7 @@ void TerminalSession::executeRole(GuardedRole role, bool allow, bool remember) case GuardedRole::ShowHostWritableStatusLine: executeShowHostWritableStatusLine(allow, remember); break; + case GuardedRole::BigPaste: applyPendingPaste(allow, remember); break; } } @@ -401,6 +402,7 @@ void TerminalSession::requestPermission(config::Permission allowedByConfig, Guar case GuardedRole::ChangeFont: emit requestPermissionForFontChange(); break; case GuardedRole::CaptureBuffer: emit requestPermissionForBufferCapture(); break; case GuardedRole::ShowHostWritableStatusLine: emit requestPermissionForShowHostWritableStatusLine(); break; + case GuardedRole::BigPaste: emit requestPermissionForPasteLargeFile(); break; // clang-format on } } @@ -651,16 +653,38 @@ void TerminalSession::pasteFromClipboard(unsigned count, bool strip) sessionLog()("pasteFromClipboard: mime data contains {} formats.", md->formats().size()); for (int i = 0; i < md->formats().size(); ++i) sessionLog()("pasteFromClipboard[{}]: {}\n", i, md->formats().at(i).toStdString()); - string const text = strip_if(normalize_crlf(clipboard->text(QClipboard::Clipboard)), strip); - if (text.empty()) + + auto const text = clipboard->text(QClipboard::Clipboard); + + // 1 MB hard limit + if (text.size() > 1024 * 1024) + { + sessionLog()("Clipboard contains huge text. Ignoring."); + _display->post([this]() { + emit showNotification("Screenshot", QString::fromStdString("Paste is too big")); + }); + return; + } + // 512 KB soft limit to ask user for permission + if (text.size() > 1024 * 512) + { + _pendingBigPaste = clipboard; + emit requestPermissionForPasteLargeFile(); + sessionLog()("Clipboard contains huge text. Requesting permission."); + return; + } + + string const strippedText = strip_if(normalize_crlf(clipboard->text(QClipboard::Clipboard)), strip); + sessionLog()("Size of text: {}", strippedText.size()); + if (strippedText.empty()) sessionLog()("Clipboard does not contain text."); else if (count == 1) - terminal().sendPaste(string_view { text }); + terminal().sendPaste(string_view { strippedText }); else { string fullPaste; for (unsigned i = 0; i < count; ++i) - fullPaste += text; + fullPaste += strippedText; terminal().sendPaste(string_view { fullPaste }); } } @@ -668,6 +692,26 @@ void TerminalSession::pasteFromClipboard(unsigned count, bool strip) sessionLog()("Could not access clipboard."); } +void TerminalSession::applyPendingPaste(bool allow, bool remember) +{ + sessionLog()("applyPendingPaste: allow={}, remember={}", allow, remember); + if (remember) + _rememberedPermissions[GuardedRole::BigPaste] = allow; + + if (!_pendingBigPaste) + return; + + if (!allow) + { + _pendingBigPaste = std::nullopt; + return; + } + + auto* clipboard = _pendingBigPaste.value(); + auto text = clipboard->text(QClipboard::Clipboard); + terminal().sendPaste(string_view { text.toStdString() }); +} + void TerminalSession::onSelectionCompleted() { switch (_config.onMouseSelection.value()) diff --git a/src/contour/TerminalSession.h b/src/contour/TerminalSession.h index 0ae7d8f79b..281640f615 100644 --- a/src/contour/TerminalSession.h +++ b/src/contour/TerminalSession.h @@ -41,6 +41,7 @@ enum class GuardedRole : uint8_t ChangeFont, CaptureBuffer, ShowHostWritableStatusLine, + BigPaste, }; /** @@ -240,6 +241,7 @@ class TerminalSession: public QAbstractItemModel, public vtbackend::Terminal::Ev void detachDisplay(display::TerminalDisplay& display); Q_INVOKABLE void applyPendingFontChange(bool allow, bool remember); + Q_INVOKABLE void applyPendingPaste(bool allow, bool remember); Q_INVOKABLE void executePendingBufferCapture(bool allow, bool remember); Q_INVOKABLE void executeShowHostWritableStatusLine(bool allow, bool remember); Q_INVOKABLE void resizeTerminalToDisplaySize(); @@ -395,6 +397,7 @@ class TerminalSession: public QAbstractItemModel, public vtbackend::Terminal::Ev void onBell(float volume); void onAlert(); void requestPermissionForFontChange(); + void requestPermissionForPasteLargeFile(); void requestPermissionForBufferCapture(); void requestPermissionForShowHostWritableStatusLine(); void showNotification(QString const& title, QString const& content); @@ -470,6 +473,7 @@ class TerminalSession: public QAbstractItemModel, public vtbackend::Terminal::Ev }; std::optional _pendingBufferCapture; std::optional _pendingFontChange; + std::optional _pendingBigPaste; PermissionCache _rememberedPermissions; std::unique_ptr _exitWatcherThread; @@ -494,6 +498,7 @@ struct std::formatter: std::formatter case contour::GuardedRole::ChangeFont: output = "Change Font"; break; case contour::GuardedRole::CaptureBuffer: output = "Capture Buffer"; break; case contour::GuardedRole::ShowHostWritableStatusLine: output = "show Host Writable Statusline"; break; + case contour::GuardedRole::BigPaste: output = "paste large number of characters"; break; } // clang-format on return formatter::format(output, ctx); diff --git a/src/contour/ui.template/Terminal.qml.in b/src/contour/ui.template/Terminal.qml.in index 962695b853..465e7872b4 100644 --- a/src/contour/ui.template/Terminal.qml.in +++ b/src/contour/ui.template/Terminal.qml.in @@ -135,6 +135,23 @@ ContourTerminal } } + + RequestPermission { + id: requestLargeFilePaste + text: "The host application is going to paste large file, are you sure?" + onYesToAllClicked: vtWidget.session.applyPendingPaste(true, true); + onYesClicked: vtWidget.session.applyPendingPaste(true, false); + onNoToAllClicked: vtWidget.session.applyPendingPaste(false, true); + onNoClicked: vtWidget.session.applyPendingPaste(false, false); + onRejected: { + console.log("[Terminal] large file paste is rejected.", vtWidget.session) + if (vtWidget.session !== null) + vtWidget.session.applyPendingPaste(false, false); + } + } + + + RequestPermission { id: requestBufferCaptureDialog text: "The host application is requesting to capture the terminal buffer." @@ -265,6 +282,7 @@ ContourTerminal vt.requestPermissionForFontChange.connect(requestFontChangeDialog.open); vt.requestPermissionForBufferCapture.connect(requestBufferCaptureDialog.open); vt.requestPermissionForShowHostWritableStatusLine.connect(requestShowHostWritableStatusLine.open); + vt.requestPermissionForPasteLargeFile.connect(requestLargeFilePaste.open); forceActiveFocus(); // TAB handling