Skip to content

Commit 969badc

Browse files
committed
libyang3: patch: needed to allow int/boolean as json strings
CESNET/libyang#2344 libyang1 would treat "true" and true as identical, same with "1234" and 1234. We need to restore this behavior so we don't break users.
1 parent 09f1fe9 commit 969badc

File tree

4 files changed

+225
-0
lines changed

4 files changed

+225
-0
lines changed

src/libyang3/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
*
22
!.gitignore
33
!Makefile
4+
!patch
5+
!patch/**

src/libyang3/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ $(addprefix $(DEST)/, $(MAIN_TARGET)): $(DEST)/% :
1919

2020
# Enable large file support for 32-bit arch
2121
echo 'add_definitions(-D_FILE_OFFSET_BITS=64)' >> CMakeLists.txt
22+
[ -d ".pc" ] && rm -rf .pc
23+
QUILT_PATCHES=../patch quilt push -a
2224

2325
ifeq ($(CROSS_BUILD_ENVIRON), y)
2426
dpkg-buildpackage -rfakeroot -d -b -us -uc -a$(CONFIGURED_ARCH) -Pcross,nocheck -j$(SONIC_CONFIG_MAKE_JOBS)
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
From 9ba71bf71bf77d4c660dabd1b05b5e5cda77cecf Mon Sep 17 00:00:00 2001
2+
From: Brad House <[email protected]>
3+
Date: Fri, 14 Feb 2025 08:54:10 -0500
4+
Subject: [PATCH] data: option to allow json int/bool as strings
5+
6+
Prior to v1.0.212 the default behavior was to allow numbers
7+
and boolean values to be in quotes, which is technically a
8+
violation of the spec.
9+
10+
This adds a new `LYD_PARSE_JSON_STRING_DATATYPES` parse option
11+
which will restore the prior behavior when enabled.
12+
13+
SONiC is using v1.0.73 currently and has a large installed base which
14+
may be in violation of the new behavior, so adding such a flag is
15+
required for this usecase.
16+
17+
Signed-off-by: Brad House <[email protected]>
18+
---
19+
src/parser_data.h | 5 ++++-
20+
src/parser_json.c | 26 ++++++++++++++++----------
21+
src/plugins_types.c | 9 ++++++---
22+
src/tree_data.h | 1 +
23+
tests/utests/data/test_parser_json.c | 15 +++++++++++++++
24+
5 files changed, 42 insertions(+), 14 deletions(-)
25+
26+
diff --git a/src/parser_data.h b/src/parser_data.h
27+
index d7fbe1815..c6371ea4a 100644
28+
--- a/src/parser_data.h
29+
+++ b/src/parser_data.h
30+
@@ -179,7 +179,10 @@ struct ly_in;
31+
#define LYD_PARSE_JSON_NULL 0x4000000 /**< Allow using JSON empty value 'null' within JSON input, such nodes are
32+
silently skipped and treated as non-existent. By default, such values
33+
are invalid. */
34+
-
35+
+#define LYD_PARSE_JSON_STRING_DATATYPES 0x8000000 /*!**< By default, JSON data values are validated to be in the proper format.
36+
+ For instance numbers are expected to not be enclosed in quotes, nor
37+
+ are boolean values. Setting this option will disable this
38+
+ validation. Prior to v1.0.212 this was the default behavior. */
39+
#define LYD_PARSE_OPTS_MASK 0xFFFF0000 /**< Mask for all the LYD_PARSE_ options. */
40+
41+
/** @} dataparseroptions */
42+
diff --git a/src/parser_json.c b/src/parser_json.c
43+
index 5c3171231..83830c0d8 100644
44+
--- a/src/parser_json.c
45+
+++ b/src/parser_json.c
46+
@@ -340,7 +340,7 @@ lydjson_get_snode(struct lyd_json_ctx *lydctx, ly_bool is_attr, const char *pref
47+
/**
48+
* @brief Get the hint for the data type parsers according to the current JSON parser context.
49+
*
50+
- * @param[in] jsonctx JSON parser context. The context is supposed to be on a value.
51+
+ * @param[in] lydctx JSON data parser context.
52+
* @param[in,out] status Pointer to the current context status,
53+
* in some circumstances the function manipulates with the context so the status is updated.
54+
* @param[out] type_hint_p Pointer to the variable to store the result.
55+
@@ -348,8 +348,9 @@ lydjson_get_snode(struct lyd_json_ctx *lydctx, ly_bool is_attr, const char *pref
56+
* @return LY_EINVAL in case of invalid context status not referring to a value.
57+
*/
58+
static LY_ERR
59+
-lydjson_value_type_hint(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *status_p, uint32_t *type_hint_p)
60+
+lydjson_value_type_hint(struct lyd_json_ctx *lydctx, enum LYJSON_PARSER_STATUS *status_p, uint32_t *type_hint_p)
61+
{
62+
+ struct lyjson_ctx *jsonctx = lydctx->jsonctx;
63+
*type_hint_p = 0;
64+
65+
if (*status_p == LYJSON_ARRAY) {
66+
@@ -383,6 +384,10 @@ lydjson_value_type_hint(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *s
67+
return LY_EINVAL;
68+
}
69+
70+
+ if (lydctx->parse_opts & LYD_PARSE_JSON_STRING_DATATYPES) {
71+
+ *type_hint_p |= LYD_VALHINT_STRING_DATATYPES;
72+
+ }
73+
+
74+
return LY_SUCCESS;
75+
}
76+
77+
@@ -391,15 +396,16 @@ lydjson_value_type_hint(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *s
78+
*
79+
* Checks for all the list's keys. Function does not revert the context state.
80+
*
81+
- * @param[in] jsonctx JSON parser context.
82+
+ * @param[in] lydctx JSON data parser context.
83+
* @param[in] list List schema node corresponding to the input data object.
84+
* @return LY_SUCCESS in case the data are ok for the @p list
85+
* @return LY_ENOT in case the input data are not sufficient to fully parse the list instance.
86+
*/
87+
static LY_ERR
88+
-lydjson_check_list(struct lyjson_ctx *jsonctx, const struct lysc_node *list)
89+
+lydjson_check_list(struct lyd_json_ctx *lydctx, const struct lysc_node *list)
90+
{
91+
LY_ERR rc = LY_SUCCESS;
92+
+ struct lyjson_ctx *jsonctx = lydctx->jsonctx;
93+
enum LYJSON_PARSER_STATUS status = lyjson_ctx_status(jsonctx);
94+
struct ly_set key_set = {0};
95+
const struct lysc_node *snode;
96+
@@ -451,7 +457,7 @@ lydjson_check_list(struct lyjson_ctx *jsonctx, const struct lysc_node *list)
97+
goto cleanup;
98+
}
99+
100+
- rc = lydjson_value_type_hint(jsonctx, &status, &hints);
101+
+ rc = lydjson_value_type_hint(lydctx, &status, &hints);
102+
LY_CHECK_GOTO(rc, cleanup);
103+
rc = ly_value_validate(NULL, snode, jsonctx->value, jsonctx->value_len, LY_VALUE_JSON, NULL, hints);
104+
LY_CHECK_GOTO(rc, cleanup);
105+
@@ -521,7 +527,7 @@ lydjson_data_check_opaq(struct lyd_json_ctx *lydctx, const struct lysc_node *sno
106+
case LYS_LEAFLIST:
107+
case LYS_LEAF:
108+
/* value may not be valid in which case we parse it as an opaque node */
109+
- if ((ret = lydjson_value_type_hint(jsonctx, &status, type_hint_p))) {
110+
+ if ((ret = lydjson_value_type_hint(lydctx, &status, type_hint_p))) {
111+
break;
112+
}
113+
114+
@@ -533,14 +539,14 @@ lydjson_data_check_opaq(struct lyd_json_ctx *lydctx, const struct lysc_node *sno
115+
break;
116+
case LYS_LIST:
117+
/* lists may not have all its keys */
118+
- if (lydjson_check_list(jsonctx, snode)) {
119+
+ if (lydjson_check_list(lydctx, snode)) {
120+
/* invalid list, parse as opaque if it misses/has invalid some keys */
121+
ret = LY_ENOT;
122+
}
123+
break;
124+
}
125+
} else if (snode->nodetype & LYD_NODE_TERM) {
126+
- ret = lydjson_value_type_hint(jsonctx, &status, type_hint_p);
127+
+ ret = lydjson_value_type_hint(lydctx, &status, type_hint_p);
128+
}
129+
130+
/* restore parser */
131+
@@ -852,7 +858,7 @@ lydjson_metadata(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, str
132+
LY_CHECK_GOTO(rc = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
133+
134+
/* get value hints */
135+
- LY_CHECK_GOTO(rc = lydjson_value_type_hint(lydctx->jsonctx, &status, &val_hints), cleanup);
136+
+ LY_CHECK_GOTO(rc = lydjson_value_type_hint(lydctx, &status, &val_hints), cleanup);
137+
138+
if (node->schema) {
139+
/* create metadata */
140+
@@ -981,7 +987,7 @@ lydjson_create_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_l
141+
dynamic = lydctx->jsonctx->dynamic;
142+
lydctx->jsonctx->dynamic = 0;
143+
144+
- LY_CHECK_RET(lydjson_value_type_hint(lydctx->jsonctx, status_inner_p, &type_hint));
145+
+ LY_CHECK_RET(lydjson_value_type_hint(lydctx, status_inner_p, &type_hint));
146+
}
147+
148+
/* get the module name */
149+
diff --git a/src/plugins_types.c b/src/plugins_types.c
150+
index d773a8a75..581ef278f 100644
151+
--- a/src/plugins_types.c
152+
+++ b/src/plugins_types.c
153+
@@ -685,7 +685,8 @@ lyplg_type_check_hints(uint32_t hints, const char *value, size_t value_len, LY_D
154+
case LY_TYPE_INT32:
155+
LY_CHECK_ARG_RET(NULL, base, LY_EINVAL);
156+
157+
- if (!(hints & (LYD_VALHINT_DECNUM | LYD_VALHINT_OCTNUM | LYD_VALHINT_HEXNUM))) {
158+
+ if (!(hints & (LYD_VALHINT_DECNUM | LYD_VALHINT_OCTNUM | LYD_VALHINT_HEXNUM)) &&
159+
+ !(hints & LYD_VALHINT_STRING_DATATYPES)) {
160+
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-number-encoded %s value \"%.*s\".",
161+
lys_datatype2str(type), (int)value_len, value);
162+
}
163+
@@ -695,7 +696,8 @@ lyplg_type_check_hints(uint32_t hints, const char *value, size_t value_len, LY_D
164+
case LY_TYPE_INT64:
165+
LY_CHECK_ARG_RET(NULL, base, LY_EINVAL);
166+
167+
- if (!(hints & LYD_VALHINT_NUM64)) {
168+
+ if (!(hints & LYD_VALHINT_NUM64) &&
169+
+ !(hints & LYD_VALHINT_STRING_DATATYPES)) {
170+
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-num64-encoded %s value \"%.*s\".",
171+
lys_datatype2str(type), (int)value_len, value);
172+
}
173+
@@ -714,7 +716,8 @@ lyplg_type_check_hints(uint32_t hints, const char *value, size_t value_len, LY_D
174+
}
175+
break;
176+
case LY_TYPE_BOOL:
177+
- if (!(hints & LYD_VALHINT_BOOLEAN)) {
178+
+ if (!(hints & LYD_VALHINT_BOOLEAN) &&
179+
+ !(hints & LYD_VALHINT_STRING_DATATYPES)) {
180+
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-boolean-encoded %s value \"%.*s\".",
181+
lys_datatype2str(type), (int)value_len, value);
182+
}
183+
diff --git a/src/tree_data.h b/src/tree_data.h
184+
index 18bc6791b..ed362a198 100644
185+
--- a/src/tree_data.h
186+
+++ b/src/tree_data.h
187+
@@ -944,6 +944,7 @@ struct lyd_node_any {
188+
#define LYD_VALHINT_NUM64 0x0010 /**< value is allowed to be an int64 or uint64 */
189+
#define LYD_VALHINT_BOOLEAN 0x0020 /**< value is allowed to be a boolean */
190+
#define LYD_VALHINT_EMPTY 0x0040 /**< value is allowed to be empty */
191+
+#define LYD_VALHINT_STRING_DATATYPES 0x0080 /**< boolean and numeric fields are allowed to be quoted */
192+
/**
193+
* @} lydvalhints
194+
*/
195+
diff --git a/tests/utests/data/test_parser_json.c b/tests/utests/data/test_parser_json.c
196+
index 1eff7819a..f752164c6 100644
197+
--- a/tests/utests/data/test_parser_json.c
198+
+++ b/tests/utests/data/test_parser_json.c
199+
@@ -168,6 +168,21 @@ test_leaf(void **state)
200+
PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, "Invalid non-string-encoded string value \"\".", "/a:foo", 1);
201+
CHECK_PARSE_LYD(data, LYD_PARSE_JSON_NULL, LYD_VALIDATE_PRESENT, tree);
202+
assert_null(tree);
203+
+
204+
+ /* validate integer in quotes errors out by default */
205+
+ data = "{\"a:foo3\":\"1234\"}";
206+
+ PARSER_CHECK_ERROR(data, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
207+
+ "Invalid non-number-encoded uint32 value \"1234\".", "/a:foo3", 1);
208+
+
209+
+ /* validate integers are parsed correctly */
210+
+ data = "{\"a:foo3\":1234}";
211+
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
212+
+ lyd_free_all(tree);
213+
+
214+
+ /* validate LYD_PARSE_JSON_STRING_DATATYPES parser flag allows integers in quotes */
215+
+ data = "{\"a:foo3\":\"1234\"}";
216+
+ CHECK_PARSE_LYD(data, LYD_PARSE_JSON_STRING_DATATYPES, LYD_VALIDATE_PRESENT, tree);
217+
+ lyd_free_all(tree);
218+
}
219+
220+
static void

src/libyang3/patch/series

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0001-pr2344-json-strings.patch

0 commit comments

Comments
 (0)