summaryrefslogtreecommitdiffstats
path: root/src/xplist.c
diff options
context:
space:
mode:
authorGravatar Sami Kortelainen2026-02-25 02:27:00 +0100
committerGravatar Nikias Bassen2026-02-25 02:27:00 +0100
commit6e03a1df6d1aa87c8f9e2b35f1a2ca60feca1c0e (patch)
treea571c74147d33da0a4dbfade178180c692c60447 /src/xplist.c
parentf5e74fc1e007b8f625d91e40c160785580de8f60 (diff)
downloadlibplist-6e03a1df6d1aa87c8f9e2b35f1a2ca60feca1c0e.tar.gz
libplist-6e03a1df6d1aa87c8f9e2b35f1a2ca60feca1c0e.tar.bz2
xplist: Enforce single root value inside <plist>
Ensure that XML property lists contain exactly one root value inside the <plist> element and reject any additional value nodes before </plist>. Add tests covering root value handling and nested CF$UID conversion behavior. Co-authored-by: Sami Kortelainen <sami.kortelainen@piceasoft.com> Co-authored-by: Nikias Bassen <nikias@gmx.li>
Diffstat (limited to 'src/xplist.c')
-rw-r--r--src/xplist.c32
1 files changed, 17 insertions, 15 deletions
diff --git a/src/xplist.c b/src/xplist.c
index a445dc5..de5227a 100644
--- a/src/xplist.c
+++ b/src/xplist.c
@@ -1170,8 +1170,9 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist)
1170 ctx->pos++; 1170 ctx->pos++;
1171 if (!strcmp(tag, "plist")) { 1171 if (!strcmp(tag, "plist")) {
1172 if (!node_path && *plist) { 1172 if (!node_path && *plist) {
1173 /* we don't allow another top-level <plist> */ 1173 PLIST_XML_ERR("Multiple top-level <plist> elements encountered\n");
1174 break; 1174 ctx->err = PLIST_ERR_PARSE;
1175 goto err_out;
1175 } 1176 }
1176 if (is_empty) { 1177 if (is_empty) {
1177 PLIST_XML_ERR("Empty plist tag\n"); 1178 PLIST_XML_ERR("Empty plist tag\n");
@@ -1403,12 +1404,6 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist)
1403 data->length = length; 1404 data->length = length;
1404 } 1405 }
1405 } else { 1406 } else {
1406 if (!strcmp(tag, "key") && !keyname && parent && (plist_get_node_type(parent) == PLIST_DICT)) {
1407 keyname = strdup("");
1408 plist_free(subnode);
1409 subnode = NULL;
1410 continue;
1411 }
1412 data->strval = strdup(""); 1407 data->strval = strdup("");
1413 data->length = 0; 1408 data->length = 0;
1414 } 1409 }
@@ -1501,14 +1496,15 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist)
1501 } 1496 }
1502 if (subnode && !closing_tag) { 1497 if (subnode && !closing_tag) {
1503 if (!*plist) { 1498 if (!*plist) {
1504 /* first node, make this node the parent node */ 1499 /* first value node inside <plist> */
1505 *plist = subnode; 1500 *plist = subnode;
1506 if (data->type != PLIST_DICT && data->type != PLIST_ARRAY) { 1501
1507 /* if the first node is not a structered node, we're done */ 1502 if (data->type == PLIST_DICT || data->type == PLIST_ARRAY) {
1508 subnode = NULL; 1503 parent = subnode;
1509 goto err_out; 1504 } else {
1505 /* scalar root: keep parsing until </plist> */
1506 parent = NULL;
1510 } 1507 }
1511 parent = subnode;
1512 } else if (parent) { 1508 } else if (parent) {
1513 switch (plist_get_node_type(parent)) { 1509 switch (plist_get_node_type(parent)) {
1514 case PLIST_DICT: 1510 case PLIST_DICT:
@@ -1528,6 +1524,11 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist)
1528 ctx->err = PLIST_ERR_PARSE; 1524 ctx->err = PLIST_ERR_PARSE;
1529 goto err_out; 1525 goto err_out;
1530 } 1526 }
1527 } else {
1528 /* We already produced root, and we're not inside a container */
1529 PLIST_XML_ERR("Unexpected tag <%s> found while </plist> is expected\n", tag);
1530 ctx->err = PLIST_ERR_PARSE;
1531 goto err_out;
1531 } 1532 }
1532 if (!is_empty && (data->type == PLIST_DICT || data->type == PLIST_ARRAY)) { 1533 if (!is_empty && (data->type == PLIST_DICT || data->type == PLIST_ARRAY)) {
1533 if (depth >= PLIST_MAX_NESTING_DEPTH) { 1534 if (depth >= PLIST_MAX_NESTING_DEPTH) {
@@ -1547,6 +1548,8 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist)
1547 1548
1548 depth++; 1549 depth++;
1549 parent = subnode; 1550 parent = subnode;
1551 } else {
1552 /* If we inserted a child scalar into a container, nothing to push. */
1550 } 1553 }
1551 subnode = NULL; 1554 subnode = NULL;
1552 } 1555 }
@@ -1587,7 +1590,6 @@ handle_closing:
1587 node_path = (struct node_path_item*)node_path->prev; 1590 node_path = (struct node_path_item*)node_path->prev;
1588 free(path_item); 1591 free(path_item);
1589 parent = (parent) ? ((node_t)parent)->parent : NULL; 1592 parent = (parent) ? ((node_t)parent)->parent : NULL;
1590 /* parent can be NULL when we just closed the root node; keep parsing */
1591 } 1593 }
1592 free(keyname); 1594 free(keyname);
1593 keyname = NULL; 1595 keyname = NULL;