This is an automated email from the git hooks/post-receive script.

git pushed a commit to branch tytab
in repository terminology.

View the commit online.

commit 33653989eb20720f2e3d581bc335b08e9c93dab6
Author: [email protected] <[email protected]>
AuthorDate: Sun Mar 29 21:54:20 2026 -0600

    feat: gate tytab tab creation behind IPC nonce lookup
    
    The tt escape sequence now carries only a nonce reference. The actual
    payload (name, direction, cmd) is retrieved from the nonce registered
    via IPC. Combined with config toggle, freeze latch, and 1s keypress
    check, this provides four independent security gates.
    
    Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---
 src/bin/win.c | 347 +++++++++++++++++++++++++++++-----------------------------
 1 file changed, 175 insertions(+), 172 deletions(-)

diff --git a/src/bin/win.c b/src/bin/win.c
index 6079f985..7afa5b42 100644
--- a/src/bin/win.c
+++ b/src/bin/win.c
@@ -6664,6 +6664,25 @@ tab_nonce_register(unsigned int term_id, const char *name,
    return strdup(buf);
 }
 
+static Tab_Nonce *
+_tab_nonce_find_and_remove(uint64_t nonce, uint32_t term_id)
+{
+   Eina_List *l;
+   Tab_Nonce *tn;
+
+   EINA_LIST_FOREACH(_pending_nonces, l, tn)
+     {
+        if (tn->nonce == nonce && tn->term_id == term_id)
+          {
+             _pending_nonces = eina_list_remove_list(_pending_nonces, l);
+             ecore_timer_del(tn->timer);
+             tn->timer = NULL;
+             return tn;
+          }
+     }
+   return NULL;
+}
+
 static Term_Container *
 _container_find_by_name(Term_Container *tc, Eina_Stringshare *name)
 {
@@ -6842,43 +6861,18 @@ _cb_command(void *data,
                   eina_stringshare_replace(&tc->name, NULL);
                }
           }
-        else if (cmd[1] == 't') // new tab: tt[;NAME;DIR[;CMD]]
+        else if (cmd[1] == 't') // execute nonce: tt;NONCE
           {
              const char *p = cmd + 2;
-             const char *name_str = NULL, *cmd_str = NULL;
-             const char *name_end = NULL;
-             char direction = 0;
-             Eina_Stringshare *name = NULL;
+             uint64_t nonce_val;
+             Tab_Nonce *tn;
              Term_Container *tc = term->container;
              Win *wn = tc->wn;
              double now;
              char buf[PATH_MAX];
              char *wdir = NULL;
 
-             /* Parse: ;NAME;DIR[;CMD] — all optional */
-             if (*p == ';')
-               {
-                  p++; /* skip first ; */
-                  name_str = p;
-
-                  name_end = strchr(p, ';');
-                  if (!name_end) return; /* malformed: need at least ;NAME;DIR */
-                  p = name_end + 1;
-
-                  /* Parse direction (may be empty) */
-                  if (*p == 'l' || *p == 'r' || *p == 't' || *p == 'b')
-                    {
-                       direction = *p;
-                       p++;
-                    }
-
-                  /* Optional CMD after ; */
-                  if (*p == ';' && *(p + 1))
-                    cmd_str = p + 1;
-               }
-             /* else: no arguments at all — current panel, default shell */
-
-             /* Anti-bombing check */
+             /* Keypress check (1s) */
              now = ecore_time_get();
              if (term->last_keypress_time == 0.0 ||
                  (now - term->last_keypress_time) > 1.0)
@@ -6889,164 +6883,173 @@ _cb_command(void *data,
                }
              term->last_keypress_time = 0.0;
 
-             /* Always inherit CWD from requesting terminal */
+             /* Parse nonce */
+             if (*p != ';' || !*(p + 1))
+               return;
+             p++;
+             nonce_val = strtoull(p, NULL, 10);
+             if (nonce_val == 0)
+               return;
+
+             /* Look up nonce */
+             tn = _tab_nonce_find_and_remove(nonce_val, term->term_id);
+             if (!tn)
+               {
+                  WRN("tab creation blocked: invalid or expired nonce");
+                  return;
+               }
+
+             /* Inherit CWD from requesting terminal */
              if (termio_cwd_get(term->termio, buf, sizeof(buf)))
                wdir = buf;
 
-             /* Build name if provided */
-             if (name_str && name_end && name_end > name_str)
-               name = eina_stringshare_add_length(name_str, name_end - name_str);
+             /* Execute using stored payload */
+             {
+                const char *cmd_str = tn->cmd;
+                char direction = tn->direction;
+                Eina_Stringshare *name = NULL;
 
-             if (name && direction)
-               {
-                  /* Full form: find named panel or create split */
-                  Term_Container *target;
-                  target = _container_find_by_name((Term_Container *)wn, name);
-                  if (target)
-                    {
-                       /* Panel exists: find attachment point */
-                       Term *tm_new;
-                       Term_Container *tc_new, *tc_attach = NULL;
+                if (tn->name)
+                  name = eina_stringshare_add(tn->name);
 
-                       if (target->type == TERM_CONTAINER_TYPE_SOLO)
-                         {
-                            if (target->parent->type != TERM_CONTAINER_TYPE_TABS)
-                              _tabs_new(target, target->parent);
-                            tc_attach = target->parent;
-                         }
-                       else if (target->type == TERM_CONTAINER_TYPE_TABS)
-                         {
-                            tc_attach = target;
-                         }
-                       else
-                         {
-                            Term *tm_first = target->term_first(target);
-                            if (tm_first)
-                              {
-                                 Term_Container *tc_first = tm_first->container;
-                                 if (tc_first->parent->type !=
-                                     TERM_CONTAINER_TYPE_TABS)
-                                   _tabs_new(tc_first, tc_first->parent);
-                                 tc_attach = tc_first->parent;
-                              }
-                         }
+                if (name && direction)
+                  {
+                     Term_Container *target;
+                     target = _container_find_by_name((Term_Container *)wn, name);
+                     if (target)
+                       {
+                          Term *tm_new;
+                          Term_Container *tc_new, *tc_attach = NULL;
 
-                       if (!tc_attach)
-                         {
-                            eina_stringshare_del(name);
-                            return;
-                         }
+                          if (target->type == TERM_CONTAINER_TYPE_SOLO)
+                            {
+                               if (target->parent->type != TERM_CONTAINER_TYPE_TABS)
+                                 _tabs_new(target, target->parent);
+                               tc_attach = target->parent;
+                            }
+                          else if (target->type == TERM_CONTAINER_TYPE_TABS)
+                            {
+                               tc_attach = target;
+                            }
+                          else
+                            {
+                               Term *tm_first = target->term_first(target);
+                               if (tm_first)
+                                 {
+                                    Term_Container *tc_first = tm_first->container;
+                                    if (tc_first->parent->type !=
+                                        TERM_CONTAINER_TYPE_TABS)
+                                      _tabs_new(tc_first, tc_first->parent);
+                                    tc_attach = tc_first->parent;
+                                 }
+                            }
 
-                       tm_new = term_new(wn, wn->config,
-                                         cmd_str, wn->config->login_shell,
-                                         wdir, 80, 24, EINA_FALSE, NULL);
-                       tc_new = _solo_new(tm_new, wn);
-                       evas_object_data_set(tm_new->termio, "sizedone",
-                                            tm_new->termio);
-                       _tabs_attach(tc_attach, tc_new);
-                    }
-                  else
-                    {
-                       /* Panel not found: split and create */
-                       Split_Direction dir;
-                       Term *tm_new;
-                       Term_Container *tc_new;
+                          if (tc_attach)
+                            {
+                               tm_new = term_new(wn, wn->config,
+                                                 cmd_str, wn->config->login_shell,
+                                                 wdir, 80, 24, EINA_FALSE, NULL);
+                               tc_new = _solo_new(tm_new, wn);
+                               evas_object_data_set(tm_new->termio, "sizedone",
+                                                    tm_new->termio);
+                               _tabs_attach(tc_attach, tc_new);
+                            }
+                       }
+                     else
+                       {
+                          Split_Direction dir;
+                          Term *tm_new;
+                          Term_Container *tc_new;
 
-                       switch (direction)
-                         {
-                          case 'l': dir = SPLIT_DIRECTION_LEFT; break;
-                          case 'r': dir = SPLIT_DIRECTION_RIGHT; break;
-                          case 't': dir = SPLIT_DIRECTION_TOP; break;
-                          case 'b': dir = SPLIT_DIRECTION_BOTTOM; break;
-                          default: eina_stringshare_del(name); return;
-                         }
+                          switch (direction)
+                            {
+                             case 'l': dir = SPLIT_DIRECTION_LEFT; break;
+                             case 'r': dir = SPLIT_DIRECTION_RIGHT; break;
+                             case 't': dir = SPLIT_DIRECTION_TOP; break;
+                             case 'b': dir = SPLIT_DIRECTION_BOTTOM; break;
+                             default: goto done;
+                            }
 
-                       if (tc->type != TERM_CONTAINER_TYPE_SOLO)
-                         {
-                            WRN("tt: requesting terminal is not in a solo "
-                                "container");
-                            eina_stringshare_del(name);
-                            return;
-                         }
+                          if (tc->type != TERM_CONTAINER_TYPE_SOLO)
+                            goto done;
 
-                       tm_new = term_new(wn, wn->config,
-                                         cmd_str, wn->config->login_shell,
-                                         wdir, 80, 24, EINA_FALSE, NULL);
-                       tc_new = _solo_new(tm_new, wn);
-                       evas_object_data_set(tm_new->termio, "sizedone",
-                                            tm_new->termio);
-                       tc->parent->split_direction(tc->parent, tc, tc_new, dir);
-                       eina_stringshare_replace(&tc_new->name, name);
-                    }
-               }
-             else if (name && !direction)
-               {
-                  /* Name provided but no direction: find panel, add tab (no split) */
-                  Term_Container *target;
-                  target = _container_find_by_name((Term_Container *)wn, name);
-                  if (target)
-                    {
-                       Term *tm_new;
-                       Term_Container *tc_new, *tc_attach = NULL;
+                          tm_new = term_new(wn, wn->config,
+                                            cmd_str, wn->config->login_shell,
+                                            wdir, 80, 24, EINA_FALSE, NULL);
+                          tc_new = _solo_new(tm_new, wn);
+                          evas_object_data_set(tm_new->termio, "sizedone",
+                                               tm_new->termio);
+                          tc->parent->split_direction(tc->parent, tc, tc_new, dir);
+                          eina_stringshare_replace(&tc_new->name, name);
+                       }
+                  }
+                else if (name && !direction)
+                  {
+                     Term_Container *target;
+                     target = _container_find_by_name((Term_Container *)wn, name);
+                     if (target)
+                       {
+                          Term *tm_new;
+                          Term_Container *tc_new, *tc_attach = NULL;
 
-                       if (target->type == TERM_CONTAINER_TYPE_SOLO)
-                         {
-                            if (target->parent->type != TERM_CONTAINER_TYPE_TABS)
-                              _tabs_new(target, target->parent);
-                            tc_attach = target->parent;
-                         }
-                       else if (target->type == TERM_CONTAINER_TYPE_TABS)
-                         {
-                            tc_attach = target;
-                         }
-                       else
-                         {
-                            Term *tm_first = target->term_first(target);
-                            if (tm_first)
-                              {
-                                 Term_Container *tc_first = tm_first->container;
-                                 if (tc_first->parent->type !=
-                                     TERM_CONTAINER_TYPE_TABS)
-                                   _tabs_new(tc_first, tc_first->parent);
-                                 tc_attach = tc_first->parent;
-                              }
-                         }
+                          if (target->type == TERM_CONTAINER_TYPE_SOLO)
+                            {
+                               if (target->parent->type != TERM_CONTAINER_TYPE_TABS)
+                                 _tabs_new(target, target->parent);
+                               tc_attach = target->parent;
+                            }
+                          else if (target->type == TERM_CONTAINER_TYPE_TABS)
+                            {
+                               tc_attach = target;
+                            }
+                          else
+                            {
+                               Term *tm_first = target->term_first(target);
+                               if (tm_first)
+                                 {
+                                    Term_Container *tc_first = tm_first->container;
+                                    if (tc_first->parent->type !=
+                                        TERM_CONTAINER_TYPE_TABS)
+                                      _tabs_new(tc_first, tc_first->parent);
+                                    tc_attach = tc_first->parent;
+                                 }
+                            }
 
-                       if (!tc_attach)
-                         {
-                            eina_stringshare_del(name);
-                            return;
-                         }
+                          if (tc_attach)
+                            {
+                               tm_new = term_new(wn, wn->config,
+                                                 cmd_str, wn->config->login_shell,
+                                                 wdir, 80, 24, EINA_FALSE, NULL);
+                               tc_new = _solo_new(tm_new, wn);
+                               evas_object_data_set(tm_new->termio, "sizedone",
+                                                    tm_new->termio);
+                               _tabs_attach(tc_attach, tc_new);
+                            }
+                       }
+                  }
+                else
+                  {
+                     Term *tm_new;
+                     Term_Container *tc_new;
 
-                       tm_new = term_new(wn, wn->config,
-                                         cmd_str, wn->config->login_shell,
-                                         wdir, 80, 24, EINA_FALSE, NULL);
-                       tc_new = _solo_new(tm_new, wn);
-                       evas_object_data_set(tm_new->termio, "sizedone",
-                                            tm_new->termio);
-                       _tabs_attach(tc_attach, tc_new);
-                    }
-                  /* else: named panel not found, no direction to create — ignore */
-               }
-             else
-               {
-                  /* No name, no direction: add tab to current panel */
-                  Term *tm_new;
-                  Term_Container *tc_new;
+                     if (tc->parent->type != TERM_CONTAINER_TYPE_TABS)
+                       _tabs_new(tc, tc->parent);
 
-                  if (tc->parent->type != TERM_CONTAINER_TYPE_TABS)
-                    _tabs_new(tc, tc->parent);
+                     tm_new = term_new(wn, wn->config,
+                                       cmd_str, wn->config->login_shell, wdir,
+                                       80, 24, EINA_FALSE, NULL);
+                     tc_new = _solo_new(tm_new, wn);
+                     evas_object_data_set(tm_new->termio, "sizedone",
+                                          tm_new->termio);
+                     _tabs_attach(tc->parent, tc_new);
+                  }
+done:
+                eina_stringshare_del(name);
+             }
 
-                  tm_new = term_new(wn, wn->config,
-                                    cmd_str, wn->config->login_shell, wdir,
-                                    80, 24, EINA_FALSE, NULL);
-                  tc_new = _solo_new(tm_new, wn);
-                  evas_object_data_set(tm_new->termio, "sizedone",
-                                       tm_new->termio);
-                  _tabs_attach(tc->parent, tc_new);
-               }
-
-             eina_stringshare_del(name);
+             free(tn->name);
+             free(tn->cmd);
+             free(tn);
           }
      }
 }

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to