fxamacker / cbor

CBOR codec (RFC 8949) with CBOR tags, Go struct tags (toarray, keyasint, omitempty), float64/32/16, big.Int, and fuzz tested billions of execs.
MIT License
748 stars 61 forks source link

bug: Question Regarding CBOR `Unmarshal` Process #578

Closed tgmtime closed 3 months ago

tgmtime commented 3 months ago

What version of fxamacker/cbor are you using?

Does this issue reproduce with the latest release?

What OS and CPU architecture are you using (go env)?

go env Output
$ go env

What did you do?

type CBORObj01 struct {
    Name string `cbor:"name"`
    Age  int    `cbor:"age"`
}

type CBORObj02 struct {
    Body   string `cbor:"1,keyasint,omitempty"`
    Status int    `cbor:"2,keyasint,omitempty"`
}

type CBORObj03 struct {
    Body   string `cbor:"1,keyasint,omitempty"`
    Status string `cbor:"2,keyasint,omitempty"`
}

func CborTest01() {
    obj_01 := CBORObj01{
        Name: "cbor test",
        Age:  21,
    }

    obj_02 := CBORObj02{}
    obj_03 := CBORObj03{}

    cborObj, err := cbor.Marshal(obj_01)
    if err != nil {
        log.Fatalf("Error creating CBOR data: %v", err)
    }

    if err := cbor.Unmarshal(cborObj, &obj_02); err != nil {
        fmt.Println("Decode error:", err)
        return
    }

    if err := cbor.Unmarshal(cborObj, &obj_03); err != nil {
        fmt.Println("Decode error:", err)
        return
    }
}

What did you expect to see?

I expected to receive errors during the Unmarshal operations when the struct definitions differ in keys and types compared to the data produced by Marshal.

What did you see instead?

Despite the differences in keys and types, the Unmarshal operations did not produce any errors. Instead, the operation succeeded, and I received unexpected results.

fxamacker commented 3 months ago

@tgmtime, you can use a decoding option to return error when destination Go struct is missing a CBOR map element.

For example, https://go.dev/play/p/jarCiNOG9RI produces expected output:

error unmarhsaling CBOR to bar: cbor: found unknown field at map element index 0

// Example code for resolving issue https://github.com/fxamacker/cbor/issues/578

package main

import (
    "fmt"

    "github.com/fxamacker/cbor/v2"
)

type Foo struct {
    Name string `cbor:"name"`
    Age  int    `cbor:"age"`
}

type Bar struct {
    Body   string `cbor:"1,keyasint,omitempty"`
    Status int    `cbor:"2,keyasint,omitempty"`
}

// MyDecodingMode supports parallel use by different goroutines and
// uses immutable settings specified in createMyDecodingMode().
var MyDecodingMode cbor.DecMode = createMyDecodingMode()

func main() {
    rejectUnknownFields()
}

// createMyDecodingMode() returns a cbor.DecMode that returns error on unknown fields
func createMyDecodingMode() cbor.DecMode {
    myDecodingOptions := cbor.DecOptions{ExtraReturnErrors: cbor.ExtraDecErrorUnknownField}
    myDecodingMode, _ := myDecodingOptions.DecMode() // error handling skipped here (not production use)
    return myDecodingMode
}

// rejectUnknownFields() uses myDecoder.Unmarshal() that returns error on unknown fields
func rejectUnknownFields() {
    foo := Foo{
        Name: "cbor test",
        Age:  21,
    }

    fooCBOR, err := cbor.Marshal(foo)
    if err != nil {
        fmt.Println("error marshaling foo to CBOR:", err)
        return
    }

    // MyDecodingMode.Unmarshal() returns error if destination Go struct is missing a CBOR map element.

    bar := Bar{}
    if err := MyDecodingMode.Unmarshal(fooCBOR, &bar); err != nil {
        fmt.Println("error unmarhsaling CBOR to bar:", err)
        return
    }
}