Skip to content

Commit aa3ae1d

Browse files
authored
parser data UPDATE add LYD_PARSE_ANYDATA_STRICT option (#2458)
* Add LYD_PARSE_ANYDATA_STRICT option Add an option to enable strict parsing for anydata subtrees in json and xml parsers. The option is exposed in yanglint command line as -A, --anydata-strict. * Formatting for the order of LYD_PARSE_ANYDATA_STRICT * Add unit tests for LYD_PARSE_ANYDATA_STRICT * Remove extra space * Fix argument formatting
1 parent 8ad0458 commit aa3ae1d

File tree

7 files changed

+131
-18
lines changed

7 files changed

+131
-18
lines changed

src/parser_data.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,10 @@ struct ly_in;
195195
format according to RFC 7951 based on their type. Using this
196196
option the validation can be softened to accept boolean and
197197
number type values enclosed in quotes. */
198+
#define LYD_PARSE_ANYDATA_STRICT 0x10000000 /**< Apply strict parsing (::LYD_PARSE_STRICT) also to anydata
199+
content. By default, unknown elements in anydata are parsed
200+
as opaque nodes. With this flag, an error is raised for any unknown
201+
elements within anydata/anyxml subtrees. */
198202
#define LYD_PARSE_OPTS_MASK 0xFFFF0000 /**< Mask for all the LYD_PARSE_ options. */
199203

200204
/** @} dataparseroptions */

src/parser_json.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1310,7 +1310,11 @@ lydjson_parse_any(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, st
13101310

13111311
/* parse any data tree with correct options, first backup the current options and then make the parser
13121312
* process data as opaq nodes */
1313-
lydctx->parse_opts &= ~LYD_PARSE_STRICT;
1313+
if (lydctx->parse_opts & LYD_PARSE_ANYDATA_STRICT) {
1314+
lydctx->parse_opts |= LYD_PARSE_STRICT;
1315+
} else {
1316+
lydctx->parse_opts &= ~LYD_PARSE_STRICT;
1317+
}
13141318
lydctx->parse_opts |= LYD_PARSE_OPAQ | (ext ? LYD_PARSE_ONLY : 0);
13151319
lydctx->int_opts |= LYD_INTOPT_ANY | LYD_INTOPT_WITH_SIBLINGS;
13161320
lydctx->any_schema = snode;

src/parser_xml.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -954,8 +954,12 @@ lydxml_subtree_any(struct lyd_xml_ctx *lydctx, const struct lysc_node *snode, co
954954
r = lyxml_ctx_next(xmlctx);
955955
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
956956

957-
/* update options so that generic data can be parsed */
958-
lydctx->parse_opts &= ~LYD_PARSE_STRICT;
957+
if (lydctx->parse_opts & LYD_PARSE_ANYDATA_STRICT) {
958+
lydctx->parse_opts |= LYD_PARSE_STRICT;
959+
} else {
960+
/* update options so that generic data can be parsed */
961+
lydctx->parse_opts &= ~LYD_PARSE_STRICT;
962+
}
959963
lydctx->parse_opts |= LYD_PARSE_OPAQ | (ext ? LYD_PARSE_ONLY : 0);
960964
lydctx->int_opts |= LYD_INTOPT_ANY | LYD_INTOPT_WITH_SIBLINGS;
961965

tests/utests/data/test_parser_json.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,35 @@ test_anydata(void **state)
376376
assert_null(tree);
377377
}
378378

379+
static void
380+
test_anydata_strict_validation(void **state)
381+
{
382+
const char *data_without_schema;
383+
const char *data_invalid;
384+
const char *data_valid;
385+
struct lyd_node *tree;
386+
387+
// no shcema defiend for "x" in the parsing context
388+
data_without_schema = "{\"a:any\":{\"x:element1\":{\"element2\":\"/a:some/a:path\",\"list\":[{},{\"key\":\"a\"}]}}}";
389+
390+
PARSER_CHECK_ERROR(data_without_schema, LYD_PARSE_ANYDATA_STRICT, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
391+
"No module named \"x\" in the context.", "/a:any", 1);
392+
393+
data_invalid = "{\"a:any\":{\"a:fooA\":{\"element2\":\"/a:some/a:path\",\"list\":[{},{\"key\":\"a\"}]}}}";
394+
395+
PARSER_CHECK_ERROR(data_invalid, LYD_PARSE_ANYDATA_STRICT, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
396+
"Node \"fooA\" not found in the \"a\" module.", "/a:any", 1);
397+
398+
data_valid = "{\"a:any\":{\"foo\":\"default-val\"}}";
399+
CHECK_PARSE_LYD(data_valid, 0, LYD_VALIDATE_PRESENT, tree);
400+
assert_non_null(tree);
401+
tree = tree->next;
402+
CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_R | LYS_SET_CONFIG, 1, "any",
403+
1, LYS_ANYDATA, 0, 0, NULL, 0);
404+
CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_SIBLINGS, data_valid);
405+
lyd_free_all(tree);
406+
}
407+
379408
static void
380409
test_anyxml(void **state)
381410
{
@@ -1031,6 +1060,7 @@ main(void)
10311060
UTEST(test_leaf, setup),
10321061
UTEST(test_leaflist, setup),
10331062
UTEST(test_anydata, setup),
1063+
UTEST(test_anydata_strict_validation, setup),
10341064
UTEST(test_anyxml, setup),
10351065
UTEST(test_list, setup),
10361066
UTEST(test_container, setup),

tests/utests/data/test_parser_xml.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,61 @@ test_anydata(void **state)
159159
lyd_free_all(tree);
160160
}
161161

162+
static void
163+
test_anydata_strict_validation(void **state)
164+
{
165+
const char *data_without_schema;
166+
const char *data_invalid;
167+
const char *data_valid;
168+
char *str;
169+
struct lyd_node *tree;
170+
171+
// no shcema defiend for "urn:tests:no:schema" in the parsing context
172+
data_without_schema = "<any xmlns=\"urn:tests:a\">\n"
173+
" <x:element1 xmlns:x=\"urn:tests:no:schema\">\n"
174+
" <x:element2>default-val</x:element2>\n"
175+
" </x:element1>\n"
176+
"</any>\n";
177+
178+
PARSER_CHECK_ERROR(data_without_schema, LYD_PARSE_ANYDATA_STRICT, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
179+
"No module with namespace \"urn:tests:no:schema\" in the context.", "/a:any", 3);
180+
181+
// anydata value are based on "module a" defined in the setup function and loaded into the parsing context.
182+
// However, the value passed in the anydata subtree is not defined in "module a".
183+
data_invalid = "<any xmlns=\"urn:tests:a\">\n"
184+
" <element1>default-val</element1>\n"
185+
"</any>\n";
186+
187+
PARSER_CHECK_ERROR(data_invalid, LYD_PARSE_ANYDATA_STRICT, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
188+
"Node \"element1\" not found in the \"a\" module.", "/a:any", 2);
189+
190+
// anydata value are based on "module a" defined in the setup function and loaded into the parsing context
191+
data_valid = "<any xmlns=\"urn:tests:a\">\n"
192+
" <foo>default-val</foo>\n"
193+
"</any>\n";
194+
195+
CHECK_PARSE_LYD(data_valid, LYD_PARSE_ANYDATA_STRICT, LYD_VALIDATE_PRESENT, tree);
196+
assert_non_null(tree);
197+
tree = tree->next;
198+
CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_R | LYS_STATUS_CURR | LYS_SET_CONFIG, 1, "any",
199+
1, LYS_ANYDATA, 0, 0, NULL, 0);
200+
201+
const char *data_expected =
202+
"<any xmlns=\"urn:tests:a\">\n"
203+
" <foo>default-val</foo>\n"
204+
"</any>\n";
205+
206+
CHECK_LYD_STRING(tree, LYD_PRINT_SIBLINGS, data_expected);
207+
208+
assert_int_equal(LY_SUCCESS, lyd_any_value_str(tree, &str));
209+
lyd_free_all(tree);
210+
211+
assert_int_equal(LY_SUCCESS, lyd_new_path2(NULL, UTEST_LYCTX, "/a:any", str, strlen(str), LYD_ANYDATA_XML, 0, &tree, NULL));
212+
free(str);
213+
CHECK_LYD_STRING(tree, LYD_PRINT_SIBLINGS, data_expected);
214+
lyd_free_all(tree);
215+
}
216+
162217
static void
163218
test_anyxml(void **state)
164219
{
@@ -1042,6 +1097,7 @@ main(void)
10421097
const struct CMUnitTest tests[] = {
10431098
UTEST(test_leaf, setup),
10441099
UTEST(test_anydata, setup),
1100+
UTEST(test_anydata_strict_validation, setup),
10451101
UTEST(test_anyxml, setup),
10461102
UTEST(test_list, setup),
10471103
UTEST(test_container, setup),

tools/lint/cmd_data.c

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,9 @@ cmd_data_help(void)
146146
" is supposed to contain the source 'nc-rpc' operation of the reply.\n"
147147
" -k, --ext-inst <name>\n"
148148
" Name of extension instance in format:\n"
149-
" <module-name>:<extension-name>:<argument>\n");
149+
" <module-name>:<extension-name>:<argument>\n"
150+
" -A, --anydata-strict\n"
151+
" Enable strict parsing of anydata content\n");
150152
cmd_data_help_format();
151153
cmd_data_help_in_format();
152154
printf(" -o OUTFILE, --output=OUTFILE\n"
@@ -161,19 +163,20 @@ cmd_data_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
161163
int rc = 0, argc = 0;
162164
int opt, opt_index;
163165
struct option options[] = {
164-
{"defaults", required_argument, NULL, 'd'},
165-
{"present", no_argument, NULL, 'e'},
166-
{"format", required_argument, NULL, 'f'},
167-
{"in-format", required_argument, NULL, 'F'},
168-
{"help", no_argument, NULL, 'h'},
169-
{"merge", no_argument, NULL, 'm'},
170-
{"output", required_argument, NULL, 'o'},
171-
{"operational", required_argument, NULL, 'O'},
172-
{"reply-rpc", required_argument, NULL, 'R'},
173-
{"not-strict", no_argument, NULL, 'n'},
174-
{"type", required_argument, NULL, 't'},
175-
{"xpath", required_argument, NULL, 'x'},
176-
{"ext-inst", required_argument, NULL, 'k'},
166+
{"defaults", required_argument, NULL, 'd'},
167+
{"present", no_argument, NULL, 'e'},
168+
{"format", required_argument, NULL, 'f'},
169+
{"in-format", required_argument, NULL, 'F'},
170+
{"help", no_argument, NULL, 'h'},
171+
{"merge", no_argument, NULL, 'm'},
172+
{"output", required_argument, NULL, 'o'},
173+
{"operational", required_argument, NULL, 'O'},
174+
{"reply-rpc", required_argument, NULL, 'R'},
175+
{"not-strict", no_argument, NULL, 'n'},
176+
{"anydata-strict", no_argument, NULL, 'A'},
177+
{"type", required_argument, NULL, 't'},
178+
{"xpath", required_argument, NULL, 'x'},
179+
{"ext-inst", required_argument, NULL, 'k'},
177180
{NULL, 0, NULL, 0}
178181
};
179182

@@ -242,6 +245,10 @@ cmd_data_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
242245
case 'n': /* --not-strict */
243246
yo->data_parse_options &= ~LYD_PARSE_STRICT;
244247
break;
248+
case 'A': /* --anydata-strict */
249+
yo->data_parse_options |= LYD_PARSE_ANYDATA_STRICT;
250+
break;
251+
245252
case 't': /* --type */
246253
if (data_type_set) {
247254
YLMSG_E("The data type (-t) cannot be set multiple times.");

tools/lint/main_ni.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@ help(int shortout)
128128
" Do not require strict data parsing (silently skip unknown data),\n"
129129
" has no effect for schemas.\n\n");
130130

131+
printf(" -A, --anydata-strict\n"
132+
" Enable strict parsing of anydata content.\n\n");
133+
131134
printf(" -e, --present Validate only with the schema modules whose data actually\n"
132135
" exist in the provided input data files. Takes effect only\n"
133136
" with the 'data' or 'config' TYPEs. Used to avoid requiring\n"
@@ -486,6 +489,7 @@ process_args(int argc, char *argv[], struct yl_opt *yo, struct ly_ctx **ctx)
486489
{"submodule", required_argument, NULL, 's'},
487490
{"ext-data", required_argument, NULL, 'x'},
488491
{"not-strict", no_argument, NULL, 'n'},
492+
{"anydata-strict", no_argument, NULL, 'A'},
489493
{"present", no_argument, NULL, 'e'},
490494
{"type", required_argument, NULL, 't'},
491495
{"default", required_argument, NULL, 'd'},
@@ -511,7 +515,7 @@ process_args(int argc, char *argv[], struct yl_opt *yo, struct ly_ctx **ctx)
511515
yo->line_length = 0;
512516

513517
opterr = 0;
514-
while ((opt = getopt_long(argc, argv, "hvVQf:I:p:DF:iP:qs:neE:t:d:lL:o:O:R:myY:XJx:G:k:", options, &opt_index)) != -1) {
518+
while ((opt = getopt_long(argc, argv, "hvVQf:I:p:DF:iP:qs:neEA:t:d:lL:o:O:R:myY:XJx:G:k:", options, &opt_index)) != -1) {
515519
switch (opt) {
516520
case 'h': /* --help */
517521
help(0);
@@ -604,6 +608,10 @@ process_args(int argc, char *argv[], struct yl_opt *yo, struct ly_ctx **ctx)
604608
yo->data_parse_options &= ~LYD_PARSE_STRICT;
605609
break;
606610

611+
case 'A': /* --anydata-strict */
612+
yo->data_parse_options |= LYD_PARSE_ANYDATA_STRICT;
613+
break;
614+
607615
case 'e': /* --present */
608616
yo->data_validate_options |= LYD_VALIDATE_PRESENT;
609617
break;

0 commit comments

Comments
 (0)