From 97438ef75c2f511c93d36b5d8210aa1e4d4e5329 Mon Sep 17 00:00:00 2001 From: Emanuele Torre Date: Tue, 15 Feb 2022 17:05:17 +0100 Subject: [PATCH] Properly print arbitrary strings as valid JSON ..instead of injecting them in the output between a pair of `"` charcters. This prevents windows with a `"` character in their class or instance name, or desktop and monitors with `"` character in their name from generating invalid or "malicious" JSON. The main problems that this was causing were: * JSON tools like jq were not able to parse the JSON output of "query -T" and "wm -d"; * if the JSON was invalid (e.g. one extra quote or `\` at the end of desktop name), bspwm would not adopt orphans after a "wm -r" command; * if the arbitrary strings contained JSON code: e.g. bspc desktop -n '","name":"hello' the JSON will be interpreted by bspwm after a reload. (in the example above, that desktop will be renamed to "hello" after a reload.) --- src/helpers.c | 15 +++++++++++++++ src/helpers.h | 1 + src/query.c | 28 +++++++++++++++++++--------- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/helpers.c b/src/helpers.c index 57a82bdd..3120ac88 100644 --- a/src/helpers.c +++ b/src/helpers.c @@ -193,3 +193,18 @@ bool is_hex_color(const char *color) } return true; } + +void print_string_as_json(FILE *f, char *str) +{ + fputc('"', f); + for (char *ptr = str; *ptr != '\0'; ++ptr) { + if (*ptr == '\x7f' || (*ptr >= '\x01' && *ptr <= '\x1f')) { + fprintf(f, "\\u%04x", *ptr); + continue; + } + if (*ptr == '"' || *ptr == '\\') + fputc('\\', f); + fputc(*ptr, f); + } + fputc('"', f); +} diff --git a/src/helpers.h b/src/helpers.h index c65ebbe5..104981a3 100644 --- a/src/helpers.h +++ b/src/helpers.h @@ -83,5 +83,6 @@ char *mktempfifo(const char *template); int asprintf(char **buf, const char *fmt, ...); int vasprintf(char **buf, const char *fmt, va_list args); bool is_hex_color(const char *color); +void print_string_as_json(FILE *f, char *str); #endif diff --git a/src/query.c b/src/query.c index aaaf3eec..1257a1c9 100644 --- a/src/query.c +++ b/src/query.c @@ -27,13 +27,14 @@ #include #include "bspwm.h" #include "desktop.h" +#include "geometry.h" +#include "helpers.h" #include "history.h" -#include "parse.h" #include "monitor.h" -#include "window.h" -#include "tree.h" +#include "parse.h" #include "query.h" -#include "geometry.h" +#include "tree.h" +#include "window.h" void query_state(FILE *rsp) { @@ -69,7 +70,9 @@ void query_state(FILE *rsp) void query_monitor(monitor_t *m, FILE *rsp) { fprintf(rsp, "{"); - fprintf(rsp, "\"name\":\"%s\",", m->name); + fprintf(rsp, "\"name\":"); + print_string_as_json(rsp, m->name); + fprintf(rsp, ","); fprintf(rsp, "\"id\":%u,", m->id); fprintf(rsp, "\"randrId\":%u,", m->randr_id); fprintf(rsp, "\"wired\":%s,", BOOL_STR(m->wired)); @@ -98,7 +101,9 @@ void query_monitor(monitor_t *m, FILE *rsp) void query_desktop(desktop_t *d, FILE *rsp) { fprintf(rsp, "{"); - fprintf(rsp, "\"name\":\"%s\",", d->name); + fprintf(rsp, "\"name\":"); + print_string_as_json(rsp, d->name); + fprintf(rsp, ","); fprintf(rsp, "\"id\":%u,", d->id); fprintf(rsp, "\"layout\":\"%s\",", LAYOUT_STR(d->layout)); fprintf(rsp, "\"userLayout\":\"%s\",", LAYOUT_STR(d->user_layout)); @@ -164,8 +169,12 @@ void query_client(client_t *c, FILE *rsp) fprintf(rsp, "null"); } else { fprintf(rsp, "{"); - fprintf(rsp, "\"className\":\"%s\",", c->class_name); - fprintf(rsp, "\"instanceName\":\"%s\",", c->instance_name); + fprintf(rsp, "\"className\":"); + print_string_as_json(rsp, c->class_name); + fprintf(rsp, ","); + fprintf(rsp, "\"instanceName\":"); + print_string_as_json(rsp, c->instance_name); + fprintf(rsp, ","); fprintf(rsp, "\"borderWidth\":%u,", c->border_width); fprintf(rsp, "\"state\":\"%s\",", STATE_STR(c->state)); fprintf(rsp, "\"lastState\":\"%s\",", STATE_STR(c->last_state)); @@ -232,7 +241,8 @@ void query_subscribers(FILE *rsp) for (subscriber_list_t *s = subscribe_head; s != NULL; s = s->next) { fprintf(rsp, "{\"fileDescriptor\": %i", fileno(s->stream)); if (s->fifo_path != NULL) { - fprintf(rsp, ",\"fifoPath\":\"%s\"", s->fifo_path); + fprintf(rsp, ",\"fifoPath\":"); + print_string_as_json(rsp, s->fifo_path); } fprintf(rsp, ",\"field\":%i,\"count\":%i}", s->field, s->count); if (s->next != NULL) {