Skip to content

Commit 9ba71bf

Browse files
committed
data: option to allow json int/bool as strings
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 03e294d commit 9ba71bf

File tree

5 files changed

+42
-14
lines changed

5 files changed

+42
-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 validated to be in the proper format.
183+
For instance numbers are expected to not be enclosed in quotes, nor
184+
are boolean values. Setting this option will disable this
185+
validation. Prior to v1.0.212 this was the default behavior. */
183186
#define LYD_PARSE_OPTS_MASK 0xFFFF0000 /**< Mask for all the LYD_PARSE_ options. */
184187

185188
/** @} dataparseroptions */

src/parser_json.c

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -340,16 +340,17 @@ 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;
353354
*type_hint_p = 0;
354355

355356
if (*status_p == LYJSON_ARRAY) {
@@ -383,6 +384,10 @@ lydjson_value_type_hint(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *s
383384
return LY_EINVAL;
384385
}
385386

387+
if (lydctx->parse_opts & LYD_PARSE_JSON_STRING_DATATYPES) {
388+
*type_hint_p |= LYD_VALHINT_STRING_DATATYPES;
389+
}
390+
386391
return LY_SUCCESS;
387392
}
388393

@@ -391,15 +396,16 @@ lydjson_value_type_hint(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *s
391396
*
392397
* Checks for all the list's keys. Function does not revert the context state.
393398
*
394-
* @param[in] jsonctx JSON parser context.
399+
* @param[in] lydctx JSON data parser context.
395400
* @param[in] list List schema node corresponding to the input data object.
396401
* @return LY_SUCCESS in case the data are ok for the @p list
397402
* @return LY_ENOT in case the input data are not sufficient to fully parse the list instance.
398403
*/
399404
static LY_ERR
400-
lydjson_check_list(struct lyjson_ctx *jsonctx, const struct lysc_node *list)
405+
lydjson_check_list(struct lyd_json_ctx *lydctx, const struct lysc_node *list)
401406
{
402407
LY_ERR rc = LY_SUCCESS;
408+
struct lyjson_ctx *jsonctx = lydctx->jsonctx;
403409
enum LYJSON_PARSER_STATUS status = lyjson_ctx_status(jsonctx);
404410
struct ly_set key_set = {0};
405411
const struct lysc_node *snode;
@@ -451,7 +457,7 @@ lydjson_check_list(struct lyjson_ctx *jsonctx, const struct lysc_node *list)
451457
goto cleanup;
452458
}
453459

454-
rc = lydjson_value_type_hint(jsonctx, &status, &hints);
460+
rc = lydjson_value_type_hint(lydctx, &status, &hints);
455461
LY_CHECK_GOTO(rc, cleanup);
456462
rc = ly_value_validate(NULL, snode, jsonctx->value, jsonctx->value_len, LY_VALUE_JSON, NULL, hints);
457463
LY_CHECK_GOTO(rc, cleanup);
@@ -521,7 +527,7 @@ lydjson_data_check_opaq(struct lyd_json_ctx *lydctx, const struct lysc_node *sno
521527
case LYS_LEAFLIST:
522528
case LYS_LEAF:
523529
/* 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))) {
530+
if ((ret = lydjson_value_type_hint(lydctx, &status, type_hint_p))) {
525531
break;
526532
}
527533

@@ -533,14 +539,14 @@ lydjson_data_check_opaq(struct lyd_json_ctx *lydctx, const struct lysc_node *sno
533539
break;
534540
case LYS_LIST:
535541
/* lists may not have all its keys */
536-
if (lydjson_check_list(jsonctx, snode)) {
542+
if (lydjson_check_list(lydctx, snode)) {
537543
/* invalid list, parse as opaque if it misses/has invalid some keys */
538544
ret = LY_ENOT;
539545
}
540546
break;
541547
}
542548
} else if (snode->nodetype & LYD_NODE_TERM) {
543-
ret = lydjson_value_type_hint(jsonctx, &status, type_hint_p);
549+
ret = lydjson_value_type_hint(lydctx, &status, type_hint_p);
544550
}
545551

546552
/* restore parser */
@@ -852,7 +858,7 @@ lydjson_metadata(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, str
852858
LY_CHECK_GOTO(rc = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
853859

854860
/* get value hints */
855-
LY_CHECK_GOTO(rc = lydjson_value_type_hint(lydctx->jsonctx, &status, &val_hints), cleanup);
861+
LY_CHECK_GOTO(rc = lydjson_value_type_hint(lydctx, &status, &val_hints), cleanup);
856862

857863
if (node->schema) {
858864
/* create metadata */
@@ -981,7 +987,7 @@ lydjson_create_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_l
981987
dynamic = lydctx->jsonctx->dynamic;
982988
lydctx->jsonctx->dynamic = 0;
983989

984-
LY_CHECK_RET(lydjson_value_type_hint(lydctx->jsonctx, status_inner_p, &type_hint));
990+
LY_CHECK_RET(lydjson_value_type_hint(lydctx, status_inner_p, &type_hint));
985991
}
986992

987993
/* 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
@@ -944,6 +944,7 @@ struct lyd_node_any {
944944
#define LYD_VALHINT_NUM64 0x0010 /**< value is allowed to be an int64 or uint64 */
945945
#define LYD_VALHINT_BOOLEAN 0x0020 /**< value is allowed to be a boolean */
946946
#define LYD_VALHINT_EMPTY 0x0040 /**< value is allowed to be empty */
947+
#define LYD_VALHINT_STRING_DATATYPES 0x0080 /**< boolean and numeric fields are allowed to be quoted */
947948
/**
948949
* @} lydvalhints
949950
*/

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)