add compile-time checks to prevent use of serialize/deserialize pragmas in CBOR

This commit is contained in:
munna0908 2025-05-31 20:36:49 +05:30
parent 50000bc0c9
commit 0096796d66
No known key found for this signature in database
GPG Key ID: 2FFCD637E937D3E6
6 changed files with 66 additions and 9 deletions

View File

@ -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:

View File

@ -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)

View File

@ -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))

View File

@ -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()

View File

@ -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

View File

@ -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))