From 0096796d660cbb4a7f176d41edb660cd79cddd60 Mon Sep 17 00:00:00 2001 From: munna0908 Date: Sat, 31 May 2025 20:36:49 +0530 Subject: [PATCH] add compile-time checks to prevent use of serialize/deserialize pragmas in CBOR --- serde/cbor/deserializer.nim | 7 +++-- serde/cbor/errors.nim | 4 --- serde/cbor/helpers.nim | 9 +++++++ serde/cbor/serializer.nim | 7 +++++ tests/cbor/testObjects.nim | 3 --- tests/cbor/testPragmaChecks.nim | 45 +++++++++++++++++++++++++++++++++ 6 files changed, 66 insertions(+), 9 deletions(-) create mode 100644 tests/cbor/testPragmaChecks.nim diff --git a/serde/cbor/deserializer.nim b/serde/cbor/deserializer.nim index da1b40b..64c67a7 100644 --- a/serde/cbor/deserializer.nim +++ b/serde/cbor/deserializer.nim @@ -607,10 +607,11 @@ proc fromCbor*[T: object](_: type T, n: CborNode): ?!T = return success T(n) expectCborKind(T, {cborMap}, n) - var res = T.default - let mode = getSerdeMode(T, deserialize) + # Added because serde {serialize, deserialize} pragmas and options are not supported cbor + assertNoPragma(T, deserialize, "deserialize pragma not supported") + try: var i: int @@ -621,6 +622,8 @@ proc fromCbor*[T: object](_: type T, n: CborNode): ?!T = else: res ): + assertNoPragma(value, deserialize, "deserialize pragma not supported") + key.text = name if not n.map.hasKey key: diff --git a/serde/cbor/errors.nim b/serde/cbor/errors.nim index 84d4c6f..20e9751 100644 --- a/serde/cbor/errors.nim +++ b/serde/cbor/errors.nim @@ -36,7 +36,3 @@ proc newUnexpectedKindError*( proc newCborError*(msg: string): ref CborParseError = newException(CborParseError, msg) - -template parseAssert*(check: bool, msg = "") = - if not check: - raise newException(CborParseError, msg) diff --git a/serde/cbor/helpers.nim b/serde/cbor/helpers.nim index 9810c6c..dc26430 100644 --- a/serde/cbor/helpers.nim +++ b/serde/cbor/helpers.nim @@ -27,6 +27,15 @@ template expectCborKind*( ) = expectCborKind(expectedType, {expectedKind}, cbor) +template parseAssert*(check: bool, msg = "") = + if not check: + raise newException(CborParseError, msg) + +template assertNoPragma*(value, pragma, msg) = + static: + when value.hasCustomPragma(pragma): + raiseAssert(msg) + macro dot*(obj: object, fld: string): untyped = ## Turn ``obj.dot("fld")`` into ``obj.fld``. newDotExpr(obj, newIdentNode(fld.strVal)) diff --git a/serde/cbor/serializer.nim b/serde/cbor/serializer.nim index ea0c241..bdfefb6 100644 --- a/serde/cbor/serializer.nim +++ b/serde/cbor/serializer.nim @@ -5,7 +5,9 @@ import std/[streams, options, tables, typetraits, math, endians, times] import pkg/questionable import pkg/questionable/results import ../utils/errors +import ../utils/pragmas import ./types +import ./helpers {.push raises: [].} @@ -265,10 +267,15 @@ proc writeCbor*(str: Stream, v: CborNode): ?!void = proc writeCbor*[T: object](str: Stream, v: T): ?!void = var n: uint + # Added because serde {serialize, deserialize} pragma and options are not supported cbor + assertNoPragma(T, serialize, "serialize pragma not supported") + for _, _ in v.fieldPairs: inc n ?str.writeInitial(5, n) + for k, f in v.fieldPairs: + assertNoPragma(f, serialize, "serialize pragma not supported") ?str.writeCbor(k) ?str.writeCbor(f) success() diff --git a/tests/cbor/testObjects.nim b/tests/cbor/testObjects.nim index 53bcc71..8c86aa0 100644 --- a/tests/cbor/testObjects.nim +++ b/tests/cbor/testObjects.nim @@ -43,9 +43,6 @@ type NewType = ref object size: uint64 - # Reference type for Inner object - InnerRef = ref Inner - # Complex object with various field types to test comprehensive serialization CompositeNested = object u: uint64 # Unsigned integer diff --git a/tests/cbor/testPragmaChecks.nim b/tests/cbor/testPragmaChecks.nim new file mode 100644 index 0000000..48ab0ef --- /dev/null +++ b/tests/cbor/testPragmaChecks.nim @@ -0,0 +1,45 @@ +import std/unittest +import std/streams + +import ../../serde/cbor +import ../../serde/utils/pragmas + +{.push raises: [].} + +suite "CBOR pragma checks": + test "fails to compile when object marked with 'serialize' pragma": + type SerializeTest {.serialize.} = object + value: int + + check not compiles(toCbor(SerializeTest(value: 42))) + + test "fails to compile when object marked with 'deserialize' pragma": + type DeserializeTest {.deserialize.} = object + value: int + + let node = CborNode(kind: cborMap) + check not compiles(DeserializeTest.fromCbor(node)) + + test "fails to compile when field marked with 'serialize' pragma": + type FieldSerializeTest = object + normalField: int + pragmaField {.serialize.}: int + + check not compiles(toCbor(FieldSerializeTest(normalField: 42, pragmaField: 100))) + + test "fails to compile when field marked with 'deserialize' pragma": + type FieldDeserializeTest = object + normalField: int + pragmaField {.deserialize.}: int + + let node = CborNode(kind: cborMap) + check not compiles(FieldDeserializeTest.fromCbor(node)) + + test "compiles when type has no pragmas": + type NoPragmaTest = object + value: int + + check compiles(toCbor(NoPragmaTest(value: 42))) + + let node = CborNode(kind: cborMap) + check compiles(NoPragmaTest.fromCbor(node))