Skip to content

Commit 775d24c

Browse files
authored
parser json UPDATE option to allow json int/bool as strings (#2344)
Prior to v1.0.212 the default behavior was to allow numbers and boolean values to be in quotes, which is technically a violation of the spec. This adds a new `LYD_PARSE_JSON_STRING_DATATYPES` parse option which will restore the prior behavior when enabled. SONiC is using v1.0.73 currently and has a large installed base which may be in violation of the new behavior, so adding such a flag is required for this usecase. Signed-off-by: Brad House <[email protected]>
1 parent 9df0098 commit 775d24c

File tree

5 files changed

+43
-14
lines changed

5 files changed

+43
-14
lines changed

src/parser_data.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,10 @@ struct ly_in;
179179
#define LYD_PARSE_JSON_NULL 0x4000000 /**< Allow using JSON empty value 'null' within JSON input, such nodes are
180180
silently skipped and treated as non-existent. By default, such values
181181
are invalid. */
182-
182+
#define LYD_PARSE_JSON_STRING_DATATYPES 0x8000000 /**< By default, JSON data values are expected to be in the correct
183+
format according to RFC 7951 based on their type. Using this
184+
option the validation can be softened to accept boolean and
185+
number type values enclosed in quotes. */
183186
#define LYD_PARSE_OPTS_MASK 0xFFFF0000 /**< Mask for all the LYD_PARSE_ options. */
184187

185188
/** @} dataparseroptions */

src/parser_json.c

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -340,16 +340,18 @@ lydjson_get_snode(struct lyd_json_ctx *lydctx, ly_bool is_attr, const char *pref
340340
/**
341341
* @brief Get the hint for the data type parsers according to the current JSON parser context.
342342
*
343-
* @param[in] jsonctx JSON parser context. The context is supposed to be on a value.
343+
* @param[in] lydctx JSON data parser context.
344344
* @param[in,out] status Pointer to the current context status,
345345
* in some circumstances the function manipulates with the context so the status is updated.
346346
* @param[out] type_hint_p Pointer to the variable to store the result.
347347
* @return LY_SUCCESS in case of success.
348348
* @return LY_EINVAL in case of invalid context status not referring to a value.
349349
*/
350350
static LY_ERR
351-
lydjson_value_type_hint(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *status_p, uint32_t *type_hint_p)
351+
lydjson_value_type_hint(struct lyd_json_ctx *lydctx, enum LYJSON_PARSER_STATUS *status_p, uint32_t *type_hint_p)
352352
{
353+
struct lyjson_ctx *jsonctx = lydctx->jsonctx;
354+
353355
*type_hint_p = 0;
354356

355357
if (*status_p == LYJSON_ARRAY) {
@@ -383,6 +385,10 @@ lydjson_value_type_hint(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *s
383385
return LY_EINVAL;
384386
}
385387

388+
if (lydctx->parse_opts & LYD_PARSE_JSON_STRING_DATATYPES) {
389+
*type_hint_p |= LYD_VALHINT_STRING_DATATYPES;
390+
}
391+
386392
return LY_SUCCESS;
387393
}
388394

@@ -391,15 +397,16 @@ lydjson_value_type_hint(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *s
391397
*
392398
* Checks for all the list's keys. Function does not revert the context state.
393399
*
394-
* @param[in] jsonctx JSON parser context.
400+
* @param[in] lydctx JSON data parser context.
395401
* @param[in] list List schema node corresponding to the input data object.
396402
* @return LY_SUCCESS in case the data are ok for the @p list
397403
* @return LY_ENOT in case the input data are not sufficient to fully parse the list instance.
398404
*/
399405
static LY_ERR
400-
lydjson_check_list(struct lyjson_ctx *jsonctx, const struct lysc_node *list)
406+
lydjson_check_list(struct lyd_json_ctx *lydctx, const struct lysc_node *list)
401407
{
402408
LY_ERR rc = LY_SUCCESS;
409+
struct lyjson_ctx *jsonctx = lydctx->jsonctx;
403410
enum LYJSON_PARSER_STATUS status = lyjson_ctx_status(jsonctx);
404411
struct ly_set key_set = {0};
405412
const struct lysc_node *snode;
@@ -451,7 +458,7 @@ lydjson_check_list(struct lyjson_ctx *jsonctx, const struct lysc_node *list)
451458
goto cleanup;
452459
}
453460

454-
rc = lydjson_value_type_hint(jsonctx, &status, &hints);
461+
rc = lydjson_value_type_hint(lydctx, &status, &hints);
455462
LY_CHECK_GOTO(rc, cleanup);
456463
rc = ly_value_validate(NULL, snode, jsonctx->value, jsonctx->value_len, LY_VALUE_JSON, NULL, hints);
457464
LY_CHECK_GOTO(rc, cleanup);
@@ -521,7 +528,7 @@ lydjson_data_check_opaq(struct lyd_json_ctx *lydctx, const struct lysc_node *sno
521528
case LYS_LEAFLIST:
522529
case LYS_LEAF:
523530
/* value may not be valid in which case we parse it as an opaque node */
524-
if ((ret = lydjson_value_type_hint(jsonctx, &status, type_hint_p))) {
531+
if ((ret = lydjson_value_type_hint(lydctx, &status, type_hint_p))) {
525532
break;
526533
}
527534

@@ -533,14 +540,14 @@ lydjson_data_check_opaq(struct lyd_json_ctx *lydctx, const struct lysc_node *sno
533540
break;
534541
case LYS_LIST:
535542
/* lists may not have all its keys */
536-
if (lydjson_check_list(jsonctx, snode)) {
543+
if (lydjson_check_list(lydctx, snode)) {
537544
/* invalid list, parse as opaque if it misses/has invalid some keys */
538545
ret = LY_ENOT;
539546
}
540547
break;
541548
}
542549
} else if (snode->nodetype & LYD_NODE_TERM) {
543-
ret = lydjson_value_type_hint(jsonctx, &status, type_hint_p);
550+
ret = lydjson_value_type_hint(lydctx, &status, type_hint_p);
544551
}
545552

546553
/* restore parser */
@@ -852,7 +859,7 @@ lydjson_metadata(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, str
852859
LY_CHECK_GOTO(rc = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
853860

854861
/* get value hints */
855-
LY_CHECK_GOTO(rc = lydjson_value_type_hint(lydctx->jsonctx, &status, &val_hints), cleanup);
862+
LY_CHECK_GOTO(rc = lydjson_value_type_hint(lydctx, &status, &val_hints), cleanup);
856863

857864
if (node->schema) {
858865
/* create metadata */
@@ -981,7 +988,7 @@ lydjson_create_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_l
981988
dynamic = lydctx->jsonctx->dynamic;
982989
lydctx->jsonctx->dynamic = 0;
983990

984-
LY_CHECK_RET(lydjson_value_type_hint(lydctx->jsonctx, status_inner_p, &type_hint));
991+
LY_CHECK_RET(lydjson_value_type_hint(lydctx, status_inner_p, &type_hint));
985992
}
986993

987994
/* get the module name */

src/plugins_types.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,8 @@ lyplg_type_check_hints(uint32_t hints, const char *value, size_t value_len, LY_D
685685
case LY_TYPE_INT32:
686686
LY_CHECK_ARG_RET(NULL, base, LY_EINVAL);
687687

688-
if (!(hints & (LYD_VALHINT_DECNUM | LYD_VALHINT_OCTNUM | LYD_VALHINT_HEXNUM))) {
688+
if (!(hints & (LYD_VALHINT_DECNUM | LYD_VALHINT_OCTNUM | LYD_VALHINT_HEXNUM)) &&
689+
!(hints & LYD_VALHINT_STRING_DATATYPES)) {
689690
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-number-encoded %s value \"%.*s\".",
690691
lys_datatype2str(type), (int)value_len, value);
691692
}
@@ -695,7 +696,8 @@ lyplg_type_check_hints(uint32_t hints, const char *value, size_t value_len, LY_D
695696
case LY_TYPE_INT64:
696697
LY_CHECK_ARG_RET(NULL, base, LY_EINVAL);
697698

698-
if (!(hints & LYD_VALHINT_NUM64)) {
699+
if (!(hints & LYD_VALHINT_NUM64) &&
700+
!(hints & LYD_VALHINT_STRING_DATATYPES)) {
699701
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-num64-encoded %s value \"%.*s\".",
700702
lys_datatype2str(type), (int)value_len, value);
701703
}
@@ -714,7 +716,8 @@ lyplg_type_check_hints(uint32_t hints, const char *value, size_t value_len, LY_D
714716
}
715717
break;
716718
case LY_TYPE_BOOL:
717-
if (!(hints & LYD_VALHINT_BOOLEAN)) {
719+
if (!(hints & LYD_VALHINT_BOOLEAN) &&
720+
!(hints & LYD_VALHINT_STRING_DATATYPES)) {
718721
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-boolean-encoded %s value \"%.*s\".",
719722
lys_datatype2str(type), (int)value_len, value);
720723
}

src/tree_data.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -945,6 +945,7 @@ struct lyd_node_any {
945945
#define LYD_VALHINT_NUM64 0x0010 /**< value is allowed to be an int64 or uint64 */
946946
#define LYD_VALHINT_BOOLEAN 0x0020 /**< value is allowed to be a boolean */
947947
#define LYD_VALHINT_EMPTY 0x0040 /**< value is allowed to be empty */
948+
#define LYD_VALHINT_STRING_DATATYPES 0x0080 /**< boolean and numeric fields are allowed to be quoted */
948949
/**
949950
* @} lydvalhints
950951
*/

tests/utests/data/test_parser_json.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,21 @@ test_leaf(void **state)
168168
PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, "Invalid non-string-encoded string value \"\".", "/a:foo", 1);
169169
CHECK_PARSE_LYD(data, LYD_PARSE_JSON_NULL, LYD_VALIDATE_PRESENT, tree);
170170
assert_null(tree);
171+
172+
/* validate integer in quotes errors out by default */
173+
data = "{\"a:foo3\":\"1234\"}";
174+
PARSER_CHECK_ERROR(data, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
175+
"Invalid non-number-encoded uint32 value \"1234\".", "/a:foo3", 1);
176+
177+
/* validate integers are parsed correctly */
178+
data = "{\"a:foo3\":1234}";
179+
CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
180+
lyd_free_all(tree);
181+
182+
/* validate LYD_PARSE_JSON_STRING_DATATYPES parser flag allows integers in quotes */
183+
data = "{\"a:foo3\":\"1234\"}";
184+
CHECK_PARSE_LYD(data, LYD_PARSE_JSON_STRING_DATATYPES, LYD_VALIDATE_PRESENT, tree);
185+
lyd_free_all(tree);
171186
}
172187

173188
static void

0 commit comments

Comments
 (0)