luci-base: uci.js: prevent sending empty uci set operations
authorJo-Philipp Wich <jo@mein.io>
Fri, 22 Jul 2022 22:10:17 +0000 (00:10 +0200)
committerJo-Philipp Wich <jo@mein.io>
Fri, 22 Jul 2022 22:10:17 +0000 (00:10 +0200)
Under certain circumstances, a staged uci option value might get unset
again before saving the config, leaving an empty section change set
behind, causing the save call to send an empty uci set request via rpc,
triggering an ubus code 4 (Resource not found) error.

In particular this prevented bridge VLANs from getting saved properly.

Fixes: #5876
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
modules/luci-base/htdocs/luci-static/resources/uci.js

index 41e902c5fe2838d2fa0135f558ae031c1acc780e..a3a0061b667d806562cfefc494542e1f12583cef 100644 (file)
@@ -2,6 +2,14 @@
 'require rpc';
 'require baseclass';
 
+function isEmpty(object, ignore) {
+       for (var property in object)
+               if (object.hasOwnProperty(property) && property != ignore)
+                       return false;
+
+       return true;
+}
+
 /**
  * @class uci
  * @memberof LuCI
@@ -570,16 +578,7 @@ return baseclass.extend(/** @lends LuCI.uci.prototype */ {
 
                        /* undelete option */
                        if (d[conf] && d[conf][sid]) {
-                               var empty = true;
-
-                               for (var key in d[conf][sid]) {
-                                       if (key != opt && d[conf][sid].hasOwnProperty(key)) {
-                                               empty = false;
-                                               break;
-                                       }
-                               }
-
-                               if (empty)
+                               if (isEmpty(d[conf][sid], opt))
                                        delete d[conf][sid];
                                else
                                        delete d[conf][sid][opt];
@@ -589,8 +588,12 @@ return baseclass.extend(/** @lends LuCI.uci.prototype */ {
                }
                else {
                        /* revert any change for to-be-deleted option */
-                       if (c[conf] && c[conf][sid])
-                               delete c[conf][sid][opt];
+                       if (c[conf] && c[conf][sid]) {
+                               if (isEmpty(c[conf][sid], opt))
+                                       delete c[conf][sid];
+                               else
+                                       delete c[conf][sid][opt];
+                       }
 
                        /* only delete existing options */
                        if (v[conf] && v[conf][sid] && v[conf][sid].hasOwnProperty(opt)) {