Skip to content

Commit d9a8d83

Browse files
Validation: add support for map key validation
This implements: - CborValidateMapIsSorted - CborValidateMapKeysAreUnique - CborValidateMapKeysAreString This completes support for all validation modes. Signed-off-by: Thiago Macieira <[email protected]>
1 parent 63d143b commit d9a8d83

File tree

4 files changed

+97
-5
lines changed

4 files changed

+97
-5
lines changed

src/cbor.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,9 @@ typedef enum CborError {
145145
CborErrorExcludedValue,
146146
CborErrorImproperValue,
147147
CborErrorOverlongEncoding,
148+
CborErrorMapKeyNotString,
149+
CborErrorMapNotSorted,
150+
CborErrorMapKeysNotUnique,
148151

149152
/* encoder errors */
150153
CborErrorTooManyItems = 768,

src/cborerrorstrings.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,16 @@ const char *cbor_error_string(CborError error)
144144
case CborErrorOverlongEncoding:
145145
return _("value encoded in non-canonical form");
146146

147+
case CborErrorMapKeyNotString:
148+
case CborErrorJsonObjectKeyNotString:
149+
return _("key in map is not a string");
150+
151+
case CborErrorMapNotSorted:
152+
return _("map is not sorted");
153+
154+
case CborErrorMapKeysNotUnique:
155+
return _("map keys are not unique");
156+
147157
case CborErrorTooManyItems:
148158
return _("too many items added to encoder");
149159

@@ -162,9 +172,6 @@ const char *cbor_error_string(CborError error)
162172
case CborErrorJsonObjectKeyIsAggregate:
163173
return _("conversion to JSON failed: key in object is an array or map");
164174

165-
case CborErrorJsonObjectKeyNotString:
166-
return _("conversion to JSON failed: key in object is not a string");
167-
168175
case CborErrorJsonNotImplemented:
169176
return _("conversion to JSON failed: open_memstream unavailable");
170177

src/cborvalidation.c

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -399,18 +399,71 @@ static inline CborError validate_floating_point(CborValue *it, CborType type, in
399399
static CborError validate_container(CborValue *it, int containerType, int flags, int recursionLeft)
400400
{
401401
CborError err;
402+
const uint8_t *previous = NULL;
403+
const uint8_t *previous_end;
404+
402405
if (!recursionLeft)
403406
return CborErrorNestingTooDeep;
404407

405408
while (!cbor_value_at_end(it)) {
409+
const uint8_t *current;
410+
411+
if (containerType == CborMapType) {
412+
current = it->ptr;
413+
if (flags & CborValidateMapKeysAreString) {
414+
CborType type = cbor_value_get_type(it);
415+
if (type == CborTagType) {
416+
/* skip the tags */
417+
CborValue copy = *it;
418+
err = cbor_value_skip_tag(&copy);
419+
if (err)
420+
return err;
421+
type = cbor_value_get_type(&copy);
422+
}
423+
if (type != CborTextStringType)
424+
return CborErrorMapKeyNotString;
425+
}
426+
}
427+
406428
err = validate_value(it, flags, recursionLeft);
407429
if (err)
408430
return err;
409431

410-
if (containerType == CborArrayType)
432+
if (containerType != CborMapType)
411433
continue;
412434

413-
/* map: that was the key, so get he value */
435+
if (flags & CborValidateMapIsSorted) {
436+
if (previous) {
437+
uint64_t len1, len2;
438+
const uint8_t *ptr;
439+
440+
/* extract the two lengths */
441+
ptr = previous;
442+
_cbor_value_extract_number(&ptr, it->parser->end, &len1);
443+
ptr = current;
444+
_cbor_value_extract_number(&ptr, it->parser->end, &len2);
445+
446+
if (len1 > len2)
447+
return CborErrorMapNotSorted;
448+
if (len1 == len2) {
449+
size_t bytelen1 = (size_t)(previous_end - previous);
450+
size_t bytelen2 = (size_t)(it->ptr - current);
451+
int r = memcmp(previous, current, bytelen1 <= bytelen2 ? bytelen1 : bytelen2);
452+
453+
if (r == 0 && bytelen1 != bytelen2)
454+
r = bytelen1 < bytelen2 ? -1 : +1;
455+
if (r > 0)
456+
return CborErrorMapNotSorted;
457+
if (r == 0 && (flags & CborValidateMapKeysAreUnique) == CborValidateMapKeysAreUnique)
458+
return CborErrorMapKeysNotUnique;
459+
}
460+
}
461+
462+
previous = current;
463+
previous_end = it->ptr;
464+
}
465+
466+
/* map: that was the key, so get the value */
414467
err = validate_value(it, flags, recursionLeft);
415468
if (err)
416469
return err;

tests/parser/tst_parser.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1669,6 +1669,14 @@ void tst_Parser::strictValidation_data()
16691669
QTest::newRow("overlong-map-24*8") << (raw("\xbb\0\0\0\0\0\0\0\x18") + mapdata24) << int(CborValidateCanonicalFormat) << CborErrorOverlongEncoding;
16701670
QTest::newRow("overlong-map-256*4") << (raw("\xba\0\0\1\0") + mapdata256) << int(CborValidateCanonicalFormat) << CborErrorOverlongEncoding;
16711671
QTest::newRow("overlong-map-256*8") << (raw("\xbb\0\0\0\0\0\0\1\0") + mapdata256) << int(CborValidateCanonicalFormat) << CborErrorOverlongEncoding;
1672+
QTest::newRow("unsorted-length-map-UU") << raw("\xa2\1\1\0\0") << int(CborValidateCanonicalFormat) << CborErrorMapNotSorted;
1673+
QTest::newRow("unsorted-length-map-UUU") << raw("\xa3\1\1\1\1\0\0") << int(CborValidateCanonicalFormat) << CborErrorMapNotSorted;
1674+
QTest::newRow("unsorted-length-map-SS") << raw("\xa2\x61z\1\x60\0") << int(CborValidateCanonicalFormat) << CborErrorMapNotSorted;
1675+
QTest::newRow("unsorted-length-map-SSS") << raw("\xa3\x61z\1\x61z\2\x60\0") << int(CborValidateCanonicalFormat) << CborErrorMapNotSorted;
1676+
QTest::newRow("unsorted-length-map-SB") << raw("\xa2\x61z\1\x40\0") << int(CborValidateCanonicalFormat) << CborErrorMapNotSorted;
1677+
QTest::newRow("unsorted-length-map-AS") << raw("\xa2\x83\0\x20\x45Hello\1\x60\0") << int(CborValidateCanonicalFormat) << CborErrorMapNotSorted;
1678+
QTest::newRow("unsorted-content-map-SS") << raw("\xa2\x61z\1\x61y\0") << int(CborValidateCanonicalFormat) << CborErrorMapNotSorted;
1679+
QTest::newRow("unsorted-content-map-AS") << raw("\xa2\x81\x21\1\x61\x21\0") << int(CborValidateCanonicalFormat) << CborErrorMapNotSorted;
16721680

16731681
QTest::newRow("tag-0") << raw("\xc0\x60") << int(CborValidateCanonicalFormat) << CborNoError;
16741682
QTest::newRow("tag-24") << raw("\xd8\x18\x40") << int(CborValidateCanonicalFormat) << CborNoError;
@@ -1749,6 +1757,10 @@ void tst_Parser::strictValidation_data()
17491757
QTest::newRow("invalid-utf8-overlong-4-5") << raw("\x65\xf8\x80\x84\x80\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString;
17501758
QTest::newRow("invalid-utf8-overlong-4-6") << raw("\x66\xfc\x80\x80\x84\x80\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString;
17511759

1760+
QTest::newRow("nonunique-content-map-UU") << raw("\xa2\0\1\0\2") << int(CborValidateStrictMode) << CborErrorMapKeysNotUnique;
1761+
QTest::newRow("nonunique-content-map-SS") << raw("\xa2\x61z\1\x61z\2") << int(CborValidateStrictMode) << CborErrorMapKeysNotUnique;
1762+
QTest::newRow("nonunique-content-map-AA") << raw("\xa2\x81\x65Hello\1\x81\x65Hello\2") << int(CborValidateStrictMode) << CborErrorMapKeysNotUnique;
1763+
17521764
QTest::newRow("tag-0-unsigned") << raw("\xc0\x00") << int(CborValidateStrictMode) << CborErrorInappropriateTagForType;
17531765
QTest::newRow("tag-0-bytearray") << raw("\xc0\x40") << int(CborValidateStrictMode) << CborErrorInappropriateTagForType;
17541766
QTest::newRow("tag-0-string") << raw("\xc0\x60") << int(CborValidateStrictMode) << CborNoError;
@@ -1828,6 +1840,23 @@ void tst_Parser::strictValidation_data()
18281840
QTest::newRow("excluded-fp--inf") << raw("\xfb\xff\xf0\0\0\0\0\0\0") << int(CborValidateFiniteFloatingPoint) << CborErrorExcludedValue;
18291841
QTest::newRow("excluded-fp-+inf") << raw("\xfb\x7f\xf0\0\0\0\0\0\0") << int(CborValidateFiniteFloatingPoint) << CborErrorExcludedValue;
18301842

1843+
// exclude non-string keys in maps
1844+
QTest::newRow("excluded-map-unsigned") << raw("\xa1\x00\1") << int(CborValidateMapKeysAreString) << CborErrorMapKeyNotString;
1845+
QTest::newRow("excluded-map-negative") << raw("\xa1\x20\1") << int(CborValidateMapKeysAreString) << CborErrorMapKeyNotString;
1846+
QTest::newRow("excluded-map-bytearray") << raw("\xa1\x40\1") << int(CborValidateMapKeysAreString) << CborErrorMapKeyNotString;
1847+
QTest::newRow("map-string") << raw("\xa1\x60\1") << int(CborValidateMapKeysAreString) << CborNoError;
1848+
QTest::newRow("map-tag-0-string") << raw("\xa1\xc0\x60\1") << int(CborValidateMapKeysAreString) << CborNoError;
1849+
QTest::newRow("excluded-map-array") << raw("\xa1\x80\1") << int(CborValidateMapKeysAreString) << CborErrorMapKeyNotString;
1850+
QTest::newRow("excluded-map-map") << raw("\xa1\xa0\1") << int(CborValidateMapKeysAreString) << CborErrorMapKeyNotString;
1851+
QTest::newRow("excluded-map-simple-0") << raw("\xa1\xe0\1") << int(CborValidateMapKeysAreString) << CborErrorMapKeyNotString;
1852+
QTest::newRow("excluded-map-false") << raw("\xa1\xf4\1") << int(CborValidateMapKeysAreString) << CborErrorMapKeyNotString;
1853+
QTest::newRow("excluded-map-true") << raw("\xa1\xf5\1") << int(CborValidateMapKeysAreString) << CborErrorMapKeyNotString;
1854+
QTest::newRow("excluded-map-null") << raw("\xa1\xf6\1") << int(CborValidateMapKeysAreString) << CborErrorMapKeyNotString;
1855+
QTest::newRow("excluded-map-undefined") << raw("\xa1\xf7\1") << int(CborValidateMapKeysAreString) << CborErrorMapKeyNotString;
1856+
QTest::newRow("excluded-map-half") << raw("\xa1\xf9\0\0\1") << int(CborValidateMapKeysAreString) << CborErrorMapKeyNotString;
1857+
QTest::newRow("excluded-map-float") << raw("\xa1\xfa\0\0\0\0\1") << int(CborValidateMapKeysAreString) << CborErrorMapKeyNotString;
1858+
QTest::newRow("excluded-map-double") << raw("\xa1\xfb\0\0\0\0\0\0\0\0\1") << int(CborValidateMapKeysAreString) << CborErrorMapKeyNotString;
1859+
18311860
// unknown simple types
18321861
QTest::newRow("unknown-simple-type-0") << raw("\xe0") << int(CborValidateNoUnknownSimpleTypes) << CborErrorUnknownSimpleType;
18331862
QTest::newRow("unknown-simple-type-32") << raw("\xf8\x20") << int(CborValidateNoUnknownSimpleTypes) << CborErrorUnknownSimpleType;

0 commit comments

Comments
 (0)