From 6f16d4d0b12ee90ec7802476b3f5129ea3e68fb8 Mon Sep 17 00:00:00 2001 From: khromenokroman Date: Wed, 7 Oct 2026 20:50:45 +0200 Subject: [PATCH 1/2] fix: empty leafref value passed validation without referred instance validate_leafref() returned OK immediately if the leafref node had no body, so an empty value was accepted even with require-instance true and no referred instance with empty value, eg: set test object object1 group "" An empty value is a value like any other: it must refer to an existing instance with the same (empty) value. Treat a missing body as an empty string both for the leafref and for the referred instances, so that an empty leafref is valid only if some referred node is also empty. The LEAFREF_OPTIMIZE binary search builds a key from the value, so use the linear search for an empty value. Also avoid printing "(null)" in the instance-required error. Fixes https://github.com/clicon/clixon/issues/647 --- lib/src/clixon_validate.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/src/clixon_validate.c b/lib/src/clixon_validate.c index 4e0c195b9..8fa38f036 100644 --- a/lib/src/clixon_validate.c +++ b/lib/src/clixon_validate.c @@ -181,7 +181,10 @@ validate_leafref_err(char *xpath, cbuf *cbm = NULL; int exist = 0; char *msg = NULL; + char *body; + if ((body = xml_body(xt)) == NULL) + body = ""; if ((cbi = cbuf_new()) == NULL){ clixon_err(OE_UNIX, errno, "cbuf_new"); goto done; @@ -195,12 +198,12 @@ validate_leafref_err(char *xpath, goto done; } cprintf(cbm, "%s", msg); - cprintf(cbi, "%s", xml_body(xt)); + cprintf(cbi, "%s", body); } else{ if (validate_errmsg(&cbm, xt, yt) < 0) goto done; - cprintf(cbi, "<%s>%s", xml_name(xt), xml_body(xt), xml_name(xt)); + cprintf(cbi, "<%s>%s", xml_name(xt), body, xml_name(xt)); } if (xret && netconf_missing_yang_xml(xret, xpath, "instance-required", cbuf_get(cbi), cbuf_get(cbm)) < 0) goto done; @@ -631,8 +634,10 @@ validate_leafref(cxobj *xt, clixon_err(OE_YANG, 0, "No argument for Y_PATH"); goto done; } + /* Empty value is a value: it must also refer to an existing (empty) instance, + * see https://github.com/clicon/clixon/issues/647 */ if ((leafrefbody = xml_body(xt)) == NULL) - goto ok; + leafrefbody = ""; if (xml_nsctx_yang(yt, &nsc) < 0) goto done; #ifdef LEAFREF_OPTIMIZE @@ -667,7 +672,8 @@ validate_leafref(cxobj *xt, &leafref_opt.lc_bin_x0) < 0) goto done; } - if (leafref_opt.lc_bin_search){ + /* Binary search requires a non-empty key, use linear search for empty value */ + if (leafref_opt.lc_bin_search && *leafrefbody != '\0'){ if ((ret = leafref_opt_search(xpath, xt, leafref_opt.lc_bin_x0, ytype, xret)) < 0) goto done; if (ret == 0) @@ -679,7 +685,7 @@ validate_leafref(cxobj *xt, for (i = 0; i < xlen; i++) { x = xvec[i]; if ((leafbody = xml_body(x)) == NULL) - continue; + leafbody = ""; if (strcmp(leafbody, leafrefbody) == 0) break; } From 4a6fb950291800659891e85396e3a868385ad82d Mon Sep 17 00:00:00 2001 From: khromenokroman Date: Wed, 7 Oct 2026 20:50:45 +0200 Subject: [PATCH 2/2] test: leafref with empty value Empty leafref to leaf-list, list key and non-key leaf must fail validation, unless the referred node has an empty value. --- test/test_leafref_empty.sh | 154 +++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100755 test/test_leafref_empty.sh diff --git a/test/test_leafref_empty.sh b/test/test_leafref_empty.sh new file mode 100755 index 000000000..5493e49f9 --- /dev/null +++ b/test/test_leafref_empty.sh @@ -0,0 +1,154 @@ +#!/usr/bin/env bash +# Yang leafref with empty value +# An empty value must refer to an existing instance with empty value, as any other value +# See https://github.com/clicon/clixon/issues/647 + +# Magic line must be first in script (see README.md) +s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi + +APPNAME=example + +cfg=$dir/conf_yang.xml +fyang=$dir/leafref.yang + +cat < $cfg + + $cfg + $dir + ${YANG_INSTALLDIR} + $fyang + /usr/local/lib/$APPNAME/clispec + /usr/local/lib/$APPNAME/cli + $APPNAME + /usr/local/var/run/$APPNAME.sock + /usr/local/var/run/$APPNAME.pidfile + /usr/local/var/$APPNAME + +EOF + +cat < $fyang +module leafref{ + yang-version 1.1; + namespace "urn:example:clixon"; + prefix ex; + container groups { + leaf-list name { + type string; + } + } + list zone { + key name; + leaf name { + type string; + } + leaf label { + type string; + } + } + container test { + list object { + key id; + leaf id { + type string; + } + leaf group { + description "leafref to leaf-list"; + type leafref { + path "/ex:groups/ex:name"; + } + } + leaf zone { + description "leafref to list key"; + type leafref { + path "/ex:zone/ex:name"; + } + } + leaf label { + description "leafref to non-key list leaf"; + type leafref { + path "/ex:zone/ex:label"; + } + } + } + } +} +EOF + +new "test params: -f $cfg" + +if [ $BE -ne 0 ]; then + new "kill old backend" + sudo clixon_backend -zf $cfg + if [ $? -ne 0 ]; then + err + fi + new "start backend -s init -f $cfg" + start_backend -s init -f $cfg +fi + +new "wait backend" +wait_backend + +new "netconf set groups and zones" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "g1g2z1z2z3" "" "" + +new "netconf commit groups and zones" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "netconf set non-empty refs" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "o1g2z2" "" "" + +new "netconf validate non-empty refs ok" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "netconf discard-changes" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "netconf set empty leafref to leaf-list" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "o1" "" "" + +new "netconf validate empty leafref to leaf-list not ok" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "applicationdata-missinginstance-required/ex:groups/ex:name" "" + +new "netconf discard-changes" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "netconf set empty leafref to list key" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "o1" "" "" + +new "netconf validate empty leafref to list key not ok" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "applicationdata-missinginstance-required/ex:zone/ex:name" "" + +new "netconf discard-changes" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "netconf set empty leafref to non-key leaf" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "o1" "" "" + +new "netconf validate empty leafref to non-key leaf not ok" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "applicationdata-missinginstance-required/ex:zone/ex:label" "" + +new "netconf set referred non-key leaf to empty value" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "z3" "" "" + +new "netconf validate empty leafref to existing empty value ok" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "netconf discard-changes" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +if [ $BE -ne 0 ]; then + new "Kill backend" + # Check if premature kill + pid=$(pgrep -u root -f clixon_backend) + if [ -z "$pid" ]; then + err "backend already dead" + fi + # kill backend + stop_backend -f $cfg +fi + +rm -rf $dir + +new "endtest" +endtest