Skip to content

Commit 93931c3

Browse files
committed
tree/load_layout: validate window_type literals strictly
Anchored ^name$ and bare names are accepted; everything else, including regex alternation, returns a clear error instead of silently falling through to ATOM_LAST.
1 parent 0bb4aba commit 93931c3

2 files changed

Lines changed: 43 additions & 21 deletions

File tree

sway/sway.5.scd

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,13 @@ runtime.
426426
*class*, *instance*, *title*, *window_role*, and *window_type* keys
427427
(which target xwayland views), each *swallows* entry may include
428428
*app_id* to match Wayland views. The *machine* key is logged and
429-
ignored. All values are PCRE2 regex strings.
429+
ignored. *class*, *instance*, *title*, *window_role* and *app_id*
430+
are PCRE2 regex strings. *window_type* must be a literal atom name
431+
(*normal*, *dialog*, *utility*, *toolbar*, *splash*, *menu*,
432+
*dropdown_menu*, *popup_menu*, *tooltip*, *notification*); the
433+
i3-save-tree anchored form _^name$_ is accepted, but regex
434+
alternation such as _^(normal|dialog)$_ is rejected, split such
435+
patterns into multiple swallows entries (which match as OR).
430436

431437
A minimal example targeting one Wayland and one xwayland window in
432438
a vertical split:

sway/tree/load_layout.c

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -227,27 +227,38 @@ static bool append_bare(char **buf, const char *key, const char *value) {
227227
return written >= 0;
228228
}
229229

230-
// i3-save-tree emits window_type as a regex-anchored enum name like
231-
// "^normal$". The criteria parser treats window_type as a bare enum token,
232-
// not a regex, so the anchored form fails. Strip a single leading ^ and a
233-
// single trailing $ before passing through.
234-
static char *unanchor_enum(const char *value) {
230+
// window_type is parsed as an enum, not a regex. Accept i3-save-tree's
231+
// "^name$" form and bare names; reject anything else (including alternation)
232+
// rather than letting the criteria parser silently fall through to ATOM_LAST.
233+
static const char *known_window_types[] = {
234+
"normal", "dialog", "utility", "toolbar", "splash", "menu",
235+
"dropdown_menu", "popup_menu", "tooltip", "notification", NULL,
236+
};
237+
238+
static char *parse_window_type_value(const char *value, char **error_out) {
235239
size_t n = strlen(value);
236-
size_t start = 0;
237-
size_t end = n;
238-
if (n > 0 && value[0] == '^') {
239-
start = 1;
240-
}
241-
if (end > start && value[end - 1] == '$') {
242-
end--;
243-
}
244-
char *out = malloc(end - start + 1);
245-
if (!out) {
240+
size_t start = (n > 0 && value[0] == '^') ? 1 : 0;
241+
size_t end = (n > start && value[n - 1] == '$') ? n - 1 : n;
242+
size_t bare_len = end - start;
243+
char *bare = malloc(bare_len + 1);
244+
if (!bare) {
245+
*error_out = format_str("append_layout: out of memory");
246246
return NULL;
247247
}
248-
memcpy(out, value + start, end - start);
249-
out[end - start] = '\0';
250-
return out;
248+
memcpy(bare, value + start, bare_len);
249+
bare[bare_len] = '\0';
250+
for (int i = 0; known_window_types[i]; i++) {
251+
if (strcasecmp(bare, known_window_types[i]) == 0) {
252+
return bare;
253+
}
254+
}
255+
*error_out = format_str("append_layout: window_type %s is not a "
256+
"supported literal value (use one of: normal, dialog, "
257+
"utility, toolbar, splash, menu, dropdown_menu, popup_menu, "
258+
"tooltip, notification; regex alternation is not supported, "
259+
"split into multiple swallow entries instead)", value);
260+
free(bare);
261+
return NULL;
251262
}
252263

253264
// app_id is a sway extension over i3's swallows schema; machine is ignored.
@@ -287,8 +298,13 @@ static struct criteria *build_swallow_criteria(struct json_object *entry,
287298
"append_layout: swallows.window_type is not a string");
288299
return NULL;
289300
}
290-
char *bare = unanchor_enum(json_object_get_string(wt));
291-
if (!bare || !append_bare(&body, "window_type", bare)) {
301+
char *bare = parse_window_type_value(json_object_get_string(wt),
302+
error_out);
303+
if (!bare) {
304+
free(body);
305+
return NULL;
306+
}
307+
if (!append_bare(&body, "window_type", bare)) {
292308
free(bare);
293309
free(body);
294310
*error_out = format_str("append_layout: out of memory");

0 commit comments

Comments
 (0)