From e38ec79be15fe355bf50670bc9066210ae0ed211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 21 Dec 2024 06:52:00 +0100 Subject: [PATCH] osc: add option to disable OSC-52, partially or fully Closes #1867 --- CHANGELOG.md | 3 +++ config.c | 24 ++++++++++++++++++++++++ config.h | 9 +++++++++ doc/foot.ini.5.scd | 27 +++++++++++++++++++++++++++ foot.ini | 3 +++ osc.c | 17 ++++++++++++++++- tests/test-config.c | 17 +++++++++++++++++ 7 files changed, 99 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b55fe9a5..bbe24067 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,6 +64,9 @@ * Box drawing characters U+1CD00...U+1CDE5 (the _"octants"_ from the _"Symbols for Legacy Computing Supplement"_ codepoint range, added in Unicode 16.0). +* `security.osc52` option, allowing you to partially or fully disable + host clipboard access via the OSC-52 escape sequence + ([#1867][1867]). ### Changed diff --git a/config.c b/config.c index 7c4ac0d1..7f1ce055 100644 --- a/config.c +++ b/config.c @@ -1110,6 +1110,25 @@ parse_section_main(struct context *ctx) } } +static bool +parse_section_security(struct context *ctx) +{ + struct config *conf = ctx->conf; + const char *key = ctx->key; + + if (streq(key, "osc52")) { + _Static_assert(sizeof(conf->security.osc52) == sizeof(int), + "enum is not 32-bit"); + return value_to_enum( + ctx, + (const char *[]){"disabled", "copy-enabled", "paste-enabled", "enabled", NULL}, + (int *)&conf->security.osc52); + } else { + LOG_CONTEXTUAL_ERR("not a valid option: %s", key); + return false; + } +} + static bool parse_section_bell(struct context *ctx) { @@ -2742,6 +2761,7 @@ parse_key_value(char *kv, const char **section, const char **key, const char **v enum section { SECTION_MAIN, + SECTION_SECURITY, SECTION_BELL, SECTION_DESKTOP_NOTIFICATIONS, SECTION_SCROLLBACK, @@ -2769,6 +2789,7 @@ static const struct { const char *name; } section_info[] = { [SECTION_MAIN] = {&parse_section_main, "main"}, + [SECTION_SECURITY] = {&parse_section_security, "security"}, [SECTION_BELL] = {&parse_section_bell, "bell"}, [SECTION_DESKTOP_NOTIFICATIONS] = {&parse_section_desktop_notifications, "desktop-notifications"}, [SECTION_SCROLLBACK] = {&parse_section_scrollback, "scrollback"}, @@ -3154,6 +3175,9 @@ config_load(struct config *conf, const char *conf_path, .underline_thickness = {.pt = 0., .px = -1}, .strikeout_thickness = {.pt = 0., .px = -1}, .dpi_aware = false, + .security = { + .osc52 = OSC52_ENABLED, + }, .bell = { .urgent = false, .notify = false, diff --git a/config.h b/config.h index adb9637c..d7192970 100644 --- a/config.h +++ b/config.h @@ -173,6 +173,15 @@ struct config { bool box_drawings_uses_font_glyphs; bool can_shape_grapheme; + struct { + enum { + OSC52_DISABLED, + OSC52_COPY_ENABLED, + OSC52_PASTE_ENABLED, + OSC52_ENABLED, + } osc52; + } security; + struct { bool urgent; bool notify; diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index 2d69f13e..ba59050e 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -416,6 +416,33 @@ The format is simply: Note: do not set *TERM* here; use the *term* option in the main (default) section instead. +# SECTION: security + +*osc52* + + Whether OSC-52 (clipboard access) is enabled or disabled. One of + *disabled*, *copy-enabled*, *paste-enabled* or *enabled*. + + OSC-52 gives terminal application access to the host clipboard + (i.e. the Wayland clipboard). This is normally not a security + issue, since all applications can access the clipboard directly + over the Wayland socket. + + However, when SSH:ing into a remote system, or accessing a + container etc, the terminal applications may be untrusted, and you + might consider disabling the host clipboard access. + + - *disabled*: disables all clipboard access + - *copy-enabled*: applications can write to the clipboard, but not + read from it. + - *paste-enabled*: applications can read from the clipboard, but + not write to it. + - *enabled*: all applications have full access to the host + clipboard. This is the default. + + Default: _enabled_ + + # SECTION: bell *urgent* diff --git a/foot.ini b/foot.ini index dccc38fe..bd4ac082 100644 --- a/foot.ini +++ b/foot.ini @@ -41,6 +41,9 @@ [environment] # name=value +[security] +# osc52=enabled # disabled|copy-enabled|paste-enabled|enabled + [bell] # urgent=no # notify=no diff --git a/osc.c b/osc.c index 72f3c366..2c02f53a 100644 --- a/osc.c +++ b/osc.c @@ -8,7 +8,7 @@ #include #define LOG_MODULE "osc" -#define LOG_ENABLE_DBG 0 +#define LOG_ENABLE_DBG 1 #include "log.h" #include "base64.h" #include "config.h" @@ -64,6 +64,14 @@ osc_to_clipboard(struct terminal *term, const char *target, return; } + const bool copy_allowed = term->conf->security.osc52 == OSC52_ENABLED + || term->conf->security.osc52 == OSC52_COPY_ENABLED; + + if (!copy_allowed) { + LOG_DBG("ignoring copy request: disabled in configuration"); + return; + } + char *decoded = base64_decode(base64_data, NULL); if (decoded == NULL) { if (errno == EINVAL) @@ -190,6 +198,13 @@ osc_from_clipboard(struct terminal *term, const char *source) return; } + const bool paste_allowed = term->conf->security.osc52 == OSC52_ENABLED + || term->conf->security.osc52 == OSC52_PASTE_ENABLED; + if (!paste_allowed) { + LOG_DBG("ignoring paste request: disabled in configuration"); + return; + } + /* Use clipboard if no source has been specified */ char src = source[0] == '\0' ? 'c' : 0; bool from_clipboard = src == 'c'; diff --git a/tests/test-config.c b/tests/test-config.c index 61999686..a189d440 100644 --- a/tests/test-config.c +++ b/tests/test-config.c @@ -553,6 +553,22 @@ test_section_main(void) config_free(&conf); } +static void +test_section_security(void) +{ + struct config conf = {0}; + struct context ctx = {.conf = &conf, .section = "security", .path = "unittest"}; + + test_invalid_key(&ctx, &parse_section_security, "invalid-key"); + test_enum( + &ctx, &parse_section_security, "osc52", 4, + (const char*[]){"disabled", "copy-enabled", "paste-enabled", "enabled"}, + (int []){OSC52_DISABLED, OSC52_COPY_ENABLED, OSC52_PASTE_ENABLED, OSC52_ENABLED}, + (int *)&conf.security.osc52); + + config_free(&conf); +} + static void test_section_bell(void) { @@ -1407,6 +1423,7 @@ main(int argc, const char *const *argv) FcInit(); log_init(LOG_COLORIZE_AUTO, false, 0, LOG_CLASS_ERROR); test_section_main(); + test_section_security(); test_section_bell(); test_section_desktop_notifications(); test_section_scrollback();