Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bug: assuming snake_case breaks JSON Schema compatibility #73

Closed
jleightcap opened this issue Aug 2, 2023 · 6 comments
Closed

bug: assuming snake_case breaks JSON Schema compatibility #73

jleightcap opened this issue Aug 2, 2023 · 6 comments

Comments

@jleightcap
Copy link

jleightcap commented Aug 2, 2023

For context, using JSON schemas generated from protobuf, which specifies that for field names (https://protobuf.dev/programming-guides/proto3/#json):

Parsers accept both the lowerCamelCase name (or the one specified by the json_name option) and the original proto field name.

In this crate, all properties seem to be normalized to use and expect snake_case. A schema might define a property

"definitions": {
  "foo": {
    "properties": {
      "publicKey": {
        ...
      },
      ...
    }
  }  
}

generating a struct

pub struct Foo {
    pub public_key: Option<...>
    # ...
}

meaning deserialization is locked to only parse snake_case artifacts, against the property definition.

(CC @woodruffw)

@woodruffw
Copy link

The JSON Schema specification for field names (https://protobuf.dev/programming-guides/proto3/#json):

Parsers accept both the lowerCamelCase name (or the one specified by the json_name option) and the original proto field name.

@jleightcap Just to clarify: this part isn't directly relevant to this report, right? The fact that we're generating the JSON Schema from a protobuf isn't actually important to this crate; the part that we're actually reporting here is that camelCase property names get normalized to snake_case, meaning that JSON documents using camelCase don't get deserialized correctly.

@jleightcap
Copy link
Author

jleightcap commented Aug 2, 2023

@jleightcap Just to clarify: this part isn't directly relevant to this report, right? The fact that we're generating the JSON Schema from a protobuf isn't actually important to this crate; the part that we're actually reporting here is that camelCase property names get normalized to snake_case, meaning that JSON documents using camelCase don't get deserialized correctly.

Yes, thank you for the clarification -- we happen to be operating from Protobuf source, but this is true in general for this naming convention clash. I updated the issue to make that more clear.

@jleightcap
Copy link
Author

jleightcap commented Aug 2, 2023

Ah, so this may be user error. With cargo expand,

    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(rename = "publicKey")]
    pub public_key: Option<...>,

The #[serde(rename = ...)] seems to already solve this. Attempting to narrow down...

@jleightcap
Copy link
Author

For a concrete example, using a schema generated from some Protobuf:

{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "$ref": "#/definitions/LogId",
    "definitions": {
        "LogId": {
            "properties": {
                "keyId": {
                    "type": "string",
                    "description": "The unique id of the log, represented as the SHA-256 hash of the log's public key, calculated over the DER encoding of the key represented as SubjectPublicKeyInfo. See https://www.rfc-editor.org/rfc/rfc6962#section-3.2",
                    "format": "binary",
                    "binaryEncoding": "base64"
                }
            },
            "additionalProperties": false,
            "type": "object",
            "title": "Log Id",
            "description": "LogId captures the identity of a transparency log."
        }
    }
}

with an associated file artifact:

{"keyId": "foo"}

Seems to work fine;

use serde::{Deserialize, Serialize};

schemafy::schemafy!("LogId.schema.json");

fn main() {
    let js = std::fs::read_to_string("LogId.json").unwrap();
    assert_eq!(
        serde_json::from_str::<LogId>(&js).unwrap(),
        LogId {
            key_id: Some(String::from("foo"))
        }
    );
}

@jleightcap
Copy link
Author

jleightcap commented Aug 2, 2023

Current failing case:

schema

{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "$ref": "#/definitions/Bundle",
    "definitions": {
        "Bundle": {
            "properties": {
                "mediaType": {
                    "type": "string",
                    "description": "MUST be application/vnd.dev.sigstore.bundle+json;version=0.1 or application/vnd.dev.sigstore.bundle+json;version=0.2 when encoded as JSON."
                },
                "verificationMaterial": {
                    "$ref": "#/definitions/dev.sigstore.bundle.v1.VerificationMaterial",
                    "additionalProperties": false,
                    "description": "When a signer is identified by a X.509 certificate, a verifier MUST verify that the signature was computed at the time the certificate was valid as described in the Sigstore client spec: \"Verification using a Bundle\". \u003chttps://docs.google.com/document/d/1kbhK2qyPPk8SLavHzYSDM8-Ueul9_oxIMVFuWMWKz0E/edit#heading=h.x8bduppe89ln\u003e"
                },
                "messageSignature": {
                    "$ref": "#/definitions/dev.sigstore.common.v1.MessageSignature",
                    "additionalProperties": false
                },
                "dsseEnvelope": {
                    "$ref": "#/definitions/io.intoto.Envelope",
                    "additionalProperties": false,
                    "description": "A DSSE envelope can contain arbitrary payloads. Verifiers must verify that the payload type is a supported and expected type. This is part of the DSSE protocol which is defined here: \u003chttps://github.com/secure-systems-lab/dsse/blob/master/protocol.md\u003e"
                }
            },
            "additionalProperties": false,
            "type": "object",
            "oneOf": [
                {
                    "required": [
                        "message_signature"
                    ]
                },
                {
                    "required": [
                        "dsse_envelope"
                    ]
                }
            ],
            "title": "Bundle"
        },
        "dev.sigstore.bundle.v1.TimestampVerificationData": {
            "properties": {
                "rfc3161Timestamps": {
                    "items": {
                        "$ref": "#/definitions/dev.sigstore.common.v1.RFC3161SignedTimestamp"
                    },
                    "additionalProperties": false,
                    "type": "array",
                    "description": "A list of RFC3161 signed timestamps provided by the user. This can be used when the entry has not been stored on a transparency log, or in conjunction for a stronger trust model. Clients MUST verify the hashed message in the message imprint against the signature in the bundle."
                }
            },
            "additionalProperties": false,
            "type": "object",
            "title": "Notes on versioning.\n The primary message ('Bundle') MUST be versioned, by populating the\n 'media_type' field. Semver-ish (only major/minor versions) scheme MUST\n be used. The current version as specified by this file is:\n application/vnd.dev.sigstore.bundle+json;version=0.2\n The semantic version is thus '0.2'.",
            "description": "Notes on versioning. The primary message ('Bundle') MUST be versioned, by populating the 'media_type' field. Semver-ish (only major/minor versions) scheme MUST be used. The current version as specified by this file is: application/vnd.dev.sigstore.bundle+json;version=0.2 The semantic version is thus '0.2'.  Various timestamped counter signatures over the artifacts signature. Currently only RFC3161 signatures are provided. More formats may be added in the future."
        },
        "dev.sigstore.bundle.v1.VerificationMaterial": {
            "properties": {
                "publicKey": {
                    "$ref": "#/definitions/dev.sigstore.common.v1.PublicKeyIdentifier",
                    "additionalProperties": false
                },
                "x509CertificateChain": {
                    "$ref": "#/definitions/dev.sigstore.common.v1.X509CertificateChain",
                    "additionalProperties": false
                },
                "tlogEntries": {
                    "items": {
                        "$ref": "#/definitions/dev.sigstore.rekor.v1.TransparencyLogEntry"
                    },
                    "additionalProperties": false,
                    "type": "array",
                    "description": "An inclusion proof and an optional signed timestamp from the log. Client verification libraries MAY provide an option to support v0.1 bundles for backwards compatibility, which may contain an inclusion promise and not an inclusion proof. In this case, the client MUST validate the promise. Verifiers SHOULD NOT allow v0.1 bundles if they're used in an ecosystem which never produced them."
                },
                "timestampVerificationData": {
                    "$ref": "#/definitions/dev.sigstore.bundle.v1.TimestampVerificationData",
                    "additionalProperties": false,
                    "description": "Timestamp may also come from tlog_entries.inclusion_promise.signed_entry_timestamp."
                }
            },
            "additionalProperties": false,
            "type": "object",
            "oneOf": [
                {
                    "required": [
                        "public_key"
                    ]
                },
                {
                    "required": [
                        "x509_certificate_chain"
                    ]
                }
            ],
            "title": "Verification Material",
            "description": "VerificationMaterial captures details on the materials used to verify signatures."
        },
        "dev.sigstore.common.v1.HashOutput": {
            "properties": {
                "algorithm": {
                    "enum": [
                        "HASH_ALGORITHM_UNSPECIFIED",
                        0,
                        "SHA2_256",
                        1
                    ],
                    "oneOf": [
                        {
                            "type": "string"
                        },
                        {
                            "type": "integer"
                        }
                    ],
                    "title": "This package defines commonly used message types within the Sigstore\n community.",
                    "description": "This package defines commonly used message types within the Sigstore community.  Only a subset of the secure hash standard algorithms are supported. See \u003chttps://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf\u003e for more details. UNSPECIFIED SHOULD not be used, primary reason for inclusion is to force any proto JSON serialization to emit the used hash algorithm, as default option is to *omit* the default value of an enum (which is the first value, represented by '0'."
                },
                "digest": {
                    "type": "string",
                    "description": "This is the raw octets of the message digest as computed by the hash algorithm.",
                    "format": "binary",
                    "binaryEncoding": "base64"
                }
            },
            "additionalProperties": false,
            "type": "object",
            "title": "Hash Output",
            "description": "HashOutput captures a digest of a 'message' (generic octet sequence) and the corresponding hash algorithm used."
        },
        "dev.sigstore.common.v1.LogId": {
            "properties": {
                "keyId": {
                    "type": "string",
                    "description": "The unique id of the log, represented as the SHA-256 hash of the log's public key, calculated over the DER encoding of the key represented as SubjectPublicKeyInfo. See https://www.rfc-editor.org/rfc/rfc6962#section-3.2",
                    "format": "binary",
                    "binaryEncoding": "base64"
                }
            },
            "additionalProperties": false,
            "type": "object",
            "title": "Log Id",
            "description": "LogId captures the identity of a transparency log."
        },
        "dev.sigstore.common.v1.MessageSignature": {
            "properties": {
                "messageDigest": {
                    "$ref": "#/definitions/dev.sigstore.common.v1.HashOutput",
                    "additionalProperties": false,
                    "description": "Message digest can be used to identify the artifact. Clients MUST NOT attempt to use this digest to verify the associated signature; it is intended solely for identification."
                },
                "signature": {
                    "type": "string",
                    "description": "The raw bytes as returned from the signature algorithm. The signature algorithm (and so the format of the signature bytes) are determined by the contents of the 'verification_material', either a key-pair or a certificate. If using a certificate, the certificate contains the required information on the signature algorithm. When using a key pair, the algorithm MUST be part of the public key, which MUST be communicated out-of-band.",
                    "format": "binary",
                    "binaryEncoding": "base64"
                }
            },
            "additionalProperties": false,
            "type": "object",
            "title": "Message Signature",
            "description": "MessageSignature stores the computed signature over a message."
        },
        "dev.sigstore.common.v1.PublicKeyIdentifier": {
            "properties": {
                "hint": {
                    "type": "string",
                    "description": "Optional unauthenticated hint on which key to use. The format of the hint must be agreed upon out of band by the signer and the verifiers, and so is not subject to this specification. Example use-case is to specify the public key to use, from a trusted key-ring. Implementors are RECOMMENDED to derive the value from the public key as described in RFC 6962. See: \u003chttps://www.rfc-editor.org/rfc/rfc6962#section-3.2\u003e"
                }
            },
            "additionalProperties": false,
            "type": "object",
            "title": "Public Key Identifier",
            "description": "PublicKeyIdentifier can be used to identify an (out of band) delivered key, to verify a signature."
        },
        "dev.sigstore.common.v1.RFC3161SignedTimestamp": {
            "properties": {
                "signedTimestamp": {
                    "type": "string",
                    "description": "Signed timestamp is the DER encoded TimeStampResponse. See https://www.rfc-editor.org/rfc/rfc3161.html#section-2.4.2",
                    "format": "binary",
                    "binaryEncoding": "base64"
                }
            },
            "additionalProperties": false,
            "type": "object",
            "title": "RFC 3161 Signed Timestamp",
            "description": "This message holds a RFC 3161 timestamp."
        },
        "dev.sigstore.common.v1.X509Certificate": {
            "properties": {
                "rawBytes": {
                    "type": "string",
                    "description": "DER-encoded X.509 certificate.",
                    "format": "binary",
                    "binaryEncoding": "base64"
                }
            },
            "additionalProperties": false,
            "type": "object",
            "title": "X 509 Certificate"
        },
        "dev.sigstore.common.v1.X509CertificateChain": {
            "properties": {
                "certificates": {
                    "items": {
                        "$ref": "#/definitions/dev.sigstore.common.v1.X509Certificate"
                    },
                    "additionalProperties": false,
                    "type": "array",
                    "description": "The chain of certificates, with indices 0 to n. The first certificate in the array must be the leaf certificate used for signing. Signers MUST NOT include their root CA certificates in their embedded certificate chains, and SHOULD NOT include intermediate CA certificates that appear in independent roots of trust. Verifiers MUST validate the chain carefully to ensure that it chains up to a root CA certificate that they trust, regardless of whether the chain includes additional intermediate/root CA certificates. Verifiers MAY enforce additional constraints, such as requiring that all intermediate CA certificates appear in an independent root of trust. Verifiers SHOULD handle old or non-complying bundles that have additional intermediate/root CA certificates."
                }
            },
            "additionalProperties": false,
            "type": "object",
            "title": "X 509 Certificate Chain",
            "description": "A chain of X.509 certificates."
        },
        "dev.sigstore.rekor.v1.Checkpoint": {
            "properties": {
                "envelope": {
                    "type": "string"
                }
            },
            "additionalProperties": false,
            "type": "object",
            "title": "Checkpoint",
            "description": "The checkpoint contains a signature of the tree head (root hash), size of the tree, the transparency log's unique identifier (log ID), hostname and the current time. The result is a string, the format is described here https://github.com/transparency-dev/formats/blob/main/log/README.md The details are here https://github.com/sigstore/rekor/blob/a6e58f72b6b18cc06cefe61808efd562b9726330/pkg/util/signed_note.go#L114 The signature has the same format as InclusionPromise.signed_entry_timestamp. See below for more details."
        },
        "dev.sigstore.rekor.v1.InclusionPromise": {
            "properties": {
                "signedEntryTimestamp": {
                    "type": "string",
                    "format": "binary",
                    "binaryEncoding": "base64"
                }
            },
            "additionalProperties": false,
            "type": "object",
            "title": "Inclusion Promise",
            "description": "The inclusion promise is calculated by Rekor. It's calculated as a signature over a canonical JSON serialization of the persisted entry, the log ID, log index and the integration timestamp. See https://github.com/sigstore/rekor/blob/a6e58f72b6b18cc06cefe61808efd562b9726330/pkg/api/entries.go#L54 The format of the signature depends on the transparency log's public key. If the signature algorithm requires a hash function and/or a signature scheme (e.g. RSA) those has to be retrieved out-of-band from the log's operators, together with the public key. This is used to verify the integration timestamp's value and that the log has promised to include the entry."
        },
        "dev.sigstore.rekor.v1.InclusionProof": {
            "properties": {
                "logIndex": {
                    "type": "string",
                    "description": "The index of the entry in the tree it was written to."
                },
                "rootHash": {
                    "type": "string",
                    "description": "The hash digest stored at the root of the merkle tree at the time the proof was generated.",
                    "format": "binary",
                    "binaryEncoding": "base64"
                },
                "treeSize": {
                    "type": "string",
                    "description": "The size of the merkle tree at the time the proof was generated."
                },
                "hashes": {
                    "items": {
                        "type": "string"
                    },
                    "type": "array",
                    "description": "A list of hashes required to compute the inclusion proof, sorted in order from leaf to root. Note that leaf and root hashes are not included. The root hash is available separately in this message, and the leaf hash should be calculated by the client.",
                    "format": "binary",
                    "binaryEncoding": "base64"
                },
                "checkpoint": {
                    "$ref": "#/definitions/dev.sigstore.rekor.v1.Checkpoint",
                    "additionalProperties": false,
                    "description": "Signature of the tree head, as of the time of this proof was generated. See above info on 'Checkpoint' for more details."
                }
            },
            "additionalProperties": false,
            "type": "object",
            "title": "Inclusion Proof",
            "description": "InclusionProof is the proof returned from the transparency log. Can be used for offline or online verification against the log."
        },
        "dev.sigstore.rekor.v1.KindVersion": {
            "properties": {
                "kind": {
                    "type": "string",
                    "description": "Kind is the type of entry being stored in the log. See here for a list: https://github.com/sigstore/rekor/tree/main/pkg/types"
                },
                "version": {
                    "type": "string",
                    "description": "The specific api version of the type."
                }
            },
            "additionalProperties": false,
            "type": "object",
            "title": "Kind Version",
            "description": "KindVersion contains the entry's kind and api version."
        },
        "dev.sigstore.rekor.v1.TransparencyLogEntry": {
            "properties": {
                "logIndex": {
                    "type": "string",
                    "description": "The global index of the entry, used when querying the log by index."
                },
                "logId": {
                    "$ref": "#/definitions/dev.sigstore.common.v1.LogId",
                    "additionalProperties": false,
                    "description": "The unique identifier of the log."
                },
                "kindVersion": {
                    "$ref": "#/definitions/dev.sigstore.rekor.v1.KindVersion",
                    "additionalProperties": false,
                    "description": "The kind (type) and version of the object associated with this entry. These values are required to construct the entry during verification."
                },
                "integratedTime": {
                    "type": "string",
                    "description": "The UNIX timestamp from the log when the entry was persisted."
                },
                "inclusionPromise": {
                    "$ref": "#/definitions/dev.sigstore.rekor.v1.InclusionPromise",
                    "additionalProperties": false,
                    "description": "The inclusion promise/signed entry timestamp from the log. Required for v0.1 bundles, and MUST be verified. Optional for \u003e= v0.2 bundles, and SHOULD be verified when present. Also may be used as a signed timestamp."
                },
                "inclusionProof": {
                    "$ref": "#/definitions/dev.sigstore.rekor.v1.InclusionProof",
                    "additionalProperties": false,
                    "description": "The inclusion proof can be used for offline or online verification that the entry was appended to the log, and that the log has not been altered."
                },
                "canonicalizedBody": {
                    "type": "string",
                    "description": "Optional. The canonicalized transparency log entry, used to reconstruct the Signed Entry Timestamp (SET) during verification. The contents of this field are the same as the `body` field in a Rekor response, meaning that it does **not** include the \"full\" canonicalized form (of log index, ID, etc.) which are exposed as separate fields. The verifier is responsible for combining the `canonicalized_body`, `log_index`, `log_id`, and `integrated_time` into the payload that the SET's signature is generated over. This field is intended to be used in cases where the SET cannot be produced determinisitically (e.g. inconsistent JSON field ordering, differing whitespace, etc). If set, clients MUST verify that the signature referenced in the `canonicalized_body` matches the signature provided in the `Bundle.content`. If not set, clients are responsible for constructing an equivalent payload from other sources to verify the signature.",
                    "format": "binary",
                    "binaryEncoding": "base64"
                }
            },
            "additionalProperties": false,
            "type": "object",
            "title": "Transparency Log Entry",
            "description": "TransparencyLogEntry captures all the details required from Rekor to reconstruct an entry, given that the payload is provided via other means. This type can easily be created from the existing response from Rekor. Future iterations could rely on Rekor returning the minimal set of attributes (excluding the payload) that are required for verifying the inclusion promise. The inclusion promise (called SignedEntryTimestamp in the response from Rekor) is similar to a Signed Certificate Timestamp as described here https://www.rfc-editor.org/rfc/rfc6962.html#section-3.2."
        },
        "io.intoto.Envelope": {
            "properties": {
                "payload": {
                    "type": "string",
                    "description": "Message to be signed. (In JSON, this is encoded as base64.) REQUIRED.",
                    "format": "binary",
                    "binaryEncoding": "base64"
                },
                "payloadType": {
                    "type": "string",
                    "description": "String unambiguously identifying how to interpret payload. REQUIRED."
                },
                "signatures": {
                    "items": {
                        "$ref": "#/definitions/io.intoto.Signature"
                    },
                    "additionalProperties": false,
                    "type": "array",
                    "description": "Signature over:     PAE(type, payload) Where PAE is defined as: PAE(type, payload) = \"DSSEv1\" + SP + LEN(type) + SP + type + SP + LEN(payload) + SP + payload +               = concatenation SP              = ASCII space [0x20] \"DSSEv1\"        = ASCII [0x44, 0x53, 0x53, 0x45, 0x76, 0x31] LEN(s)          = ASCII decimal encoding of the byte length of s, with no leading zeros REQUIRED (length \u003e= 1)."
                }
            },
            "additionalProperties": false,
            "type": "object",
            "title": "Envelope",
            "description": "An authenticated message of arbitrary type."
        },
        "io.intoto.Signature": {
            "properties": {
                "sig": {
                    "type": "string",
                    "description": "Signature itself. (In JSON, this is encoded as base64.) REQUIRED.",
                    "format": "binary",
                    "binaryEncoding": "base64"
                },
                "keyid": {
                    "type": "string",
                    "description": "*Unauthenticated* hint identifying which public key was used. OPTIONAL."
                }
            },
            "additionalProperties": false,
            "type": "object",
            "title": "Signature"
        }
    }
}
JSON artifact

{"mediaType": "application/vnd.dev.sigstore.bundle+json;version=0.1", "verificationMaterial": {"x509CertificateChain": {"certificates": [{"rawBytes": "MIIC5zCCAmygAwIBAgIUJ3vpewdf6e91rgjqCqagstF4qn8wCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjMwNDI2MDAyMTA4WhcNMjMwNDI2MDAzMTA4WjAAMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE2sd6+lOBcn5MXtnbwca7zcwpprl7GUZiKTO9IWpAUfVTtx+BXGHQCRwsFy/d7dLlf4hurIqhzMD5yaC2kcU9/8c9G55JyBXF8Dx5SQm9y2rPWFIdm29Ql9A3I3yyEFyPo4IBbjCCAWowDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMB0GA1UdDgQWBBTlaUfjpiXGhBP3hOCW0JJZDSPxgzAfBgNVHSMEGDAWgBRxhjCmFHxib/n31vQFGn9f/+tvrDAYBgNVHREBAf8EDjAMgQphQHRueS50b3duMCwGCisGAQQBg78wAQEEHmh0dHBzOi8vZ2l0aHViLmNvbS9sb2dpbi9vYXV0aDAuBgorBgEEAYO/MAEIBCAMHmh0dHBzOi8vZ2l0aHViLmNvbS9sb2dpbi9vYXV0aDCBigYKKwYBBAHWeQIEAgR8BHoAeAB2ACswvNxoiMni4dgmKV50H0g5MZYC8pwzy15DQP6yrIZ6AAABh7rveBsAAAQDAEcwRQIhAKOZPMN9Q9qO1HXigHBPt+Ic16yy2Zgv2KQ23i5WLj16AiAzrFpuayGXdoK+hYePl9dEeXjG/vB2jK/E3sEsIrXtETAKBggqhkjOPQQDAwNpADBmAjEAgmhg80mI/Scr0isBnD5FYXZ8WxA8tnBBPmdf4aNGForGazGXaFQVPXgBVPv+YGI/AjEA0QzPC5dHD/WWXW2GbEC4dpwFk8OGRkiExMOy/+CqabbVg+/lx1N9VGBTlUTft45d"}]}, "tlogEntries": [{"logIndex": "7390977", "logId": {"keyId": "0y8wo8MtY5wrdiIFohx7sHeI5oKDpK5vQhGHI6G+pJY="}, "kindVersion": {"kind": "hashedrekord", "version": "0.0.1"}, "integratedTime": "1682468469", "inclusionPromise": {"signedEntryTimestamp": "MEUCICSJs5PgN4W3Lku3ybrwfNLAKMWaOvffg2tnqm19VrWEAiEA16MVPsWDoaAljsxGefpQazpvYfs1pv8lzdgZQ0I4rH0="}, "inclusionProof": {"logIndex": "7376158", "rootHash": "LE67t2Zlc0g35az81xMg0cgM2DULj8fNsGGHTcRthcs=", "treeSize": "7376159", "hashes": ["zgesNHwk09VvW4IDaPrJMtX59glNyyLPzeJO1Gw1hCI=", "lJiFr9ZP5FO8BjqLAUQ16A/0/LoOOQ0gfeNhdxaxO2w=", "sMImd51DBHQnH1tz4sGk8gXB+FjWyusVXbP0GmpFnB4=", "cDU1nEpl0WCRlxLi/gNVzykDzobU4qG/7BQZxn0qDgU=", "4CRqWzG3qpxKvlHuZg5O6QjQiwOzerbjwsAh30EVlA8=", "Ru0p3GE/zB2zub2/xR5rY/aM4J+5VJmiIuIl2enF/ws=", "2W+NG5yGR68lrLGcw4gn9CSCfeQF98d3LMfdo8tPyok=", "bEs1eYxy9R6hR2veGEwYW4PEdrZKrdqZ7uDlmmNtlas=", "sgQMnwcK7VxxAi+fygxq8iJ+zWqShjXm07/AWobWcXU=", "y4BESazXFcefRzxpN1PfJHoqRaKnPJPM5H/jotx0QY8=", "xiNEdLOpmGQERCR+DCEFVRK+Ns6G0BLV9M6sQQkRhik="], "checkpoint": {"envelope": "rekor.sigstage.dev - 8050909264565447525\n7376159\nLE67t2Zlc0g35az81xMg0cgM2DULj8fNsGGHTcRthcs=\nTimestamp: 1682468469199678948\n\n\u2014 rekor.sigstage.dev 0y8wozBEAiBbAodz3dBqJjGMhnZEkbaTDVxc8+tBEPKbaWUZoqxFvwIgGtYzFgFaM3UXBRHmzgmcrCxA145dpQ2YD0yFqiPHO7U=\n"}}, "canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI4MDJkZDYwZmY4ODMzMzgwMmYyNTg1ZTczMDQzYmQyMWMzNDEyODVlMTk5MmZlNWIzMTc1NWUxY2FkZWFlMzBlIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1HVUNNUUNPT0pxVFk2WFdnQjY0aXpLMldWUDA3YjBTRzlNNVdQQ3dLaGZUUHdNdnRzZ1VpOEtlUkd3UWt2dkxZYktIZHFVQ01FYk9YRkcwTk1xRVF4V1ZiNnJtR25leGRBRHVHZjZKbDhxQUM4dG42N3AzUWZWb1h6TXZGQTYxUHp4d1Z3dmI4Zz09IiwicHVibGljS2V5Ijp7ImNvbnRlbnQiOiJMUzB0TFMxQ1JVZEpUaUJEUlZKVVNVWkpRMEZVUlMwdExTMHRDazFKU1VNMWVrTkRRVzE1WjBGM1NVSkJaMGxWU2pOMmNHVjNaR1kyWlRreGNtZHFjVU54WVdkemRFWTBjVzQ0ZDBObldVbExiMXBKZW1vd1JVRjNUWGNLVG5wRlZrMUNUVWRCTVZWRlEyaE5UV015Ykc1ak0xSjJZMjFWZFZwSFZqSk5ValIzU0VGWlJGWlJVVVJGZUZaNllWZGtlbVJIT1hsYVV6RndZbTVTYkFwamJURnNXa2RzYUdSSFZYZElhR05PVFdwTmQwNUVTVEpOUkVGNVRWUkJORmRvWTA1TmFrMTNUa1JKTWsxRVFYcE5WRUUwVjJwQlFVMUlXWGRGUVZsSUNrdHZXa2w2YWpCRFFWRlpSa3MwUlVWQlEwbEVXV2RCUlRKelpEWXJiRTlDWTI0MVRWaDBibUozWTJFM2VtTjNjSEJ5YkRkSFZWcHBTMVJQT1VsWGNFRUtWV1pXVkhSNEswSllSMGhSUTFKM2MwWjVMMlEzWkV4c1pqUm9kWEpKY1doNlRVUTFlV0ZETW10alZUa3ZPR001UnpVMVNubENXRVk0UkhnMVUxRnRPUXA1TW5KUVYwWkpaRzB5T1ZGc09VRXpTVE41ZVVWR2VWQnZORWxDWW1wRFEwRlhiM2RFWjFsRVZsSXdVRUZSU0M5Q1FWRkVRV2RsUVUxQ1RVZEJNVlZrQ2twUlVVMU5RVzlIUTBOelIwRlJWVVpDZDAxRVRVSXdSMEV4VldSRVoxRlhRa0pVYkdGVlptcHdhVmhIYUVKUU0yaFBRMWN3U2twYVJGTlFlR2Q2UVdZS1FtZE9Wa2hUVFVWSFJFRlhaMEpTZUdocVEyMUdTSGhwWWk5dU16RjJVVVpIYmpsbUx5dDBkbkpFUVZsQ1owNVdTRkpGUWtGbU9FVkVha0ZOWjFGd2FBcFJTRkoxWlZNMU1HSXpaSFZOUTNkSFEybHpSMEZSVVVKbk56aDNRVkZGUlVodGFEQmtTRUo2VDJrNGRsb3liREJoU0ZacFRHMU9kbUpUT1hOaU1tUndDbUpwT1haWldGWXdZVVJCZFVKbmIzSkNaMFZGUVZsUEwwMUJSVWxDUTBGTlNHMW9NR1JJUW5wUGFUaDJXakpzTUdGSVZtbE1iVTUyWWxNNWMySXlaSEFLWW1rNWRsbFlWakJoUkVOQ2FXZFpTMHQzV1VKQ1FVaFhaVkZKUlVGblVqaENTRzlCWlVGQ01rRkRjM2QyVG5odmFVMXVhVFJrWjIxTFZqVXdTREJuTlFwTldsbERPSEIzZW5reE5VUlJVRFo1Y2tsYU5rRkJRVUpvTjNKMlpVSnpRVUZCVVVSQlJXTjNVbEZKYUVGTFQxcFFUVTQ1VVRseFR6RklXR2xuU0VKUUNuUXJTV014Tm5sNU1scG5kakpMVVRJemFUVlhUR294TmtGcFFYcHlSbkIxWVhsSFdHUnZTeXRvV1dWUWJEbGtSV1ZZYWtjdmRrSXlha3N2UlROelJYTUtTWEpZZEVWVVFVdENaMmR4YUd0cVQxQlJVVVJCZDA1d1FVUkNiVUZxUlVGbmJXaG5PREJ0U1M5VFkzSXdhWE5DYmtRMVJsbFlXamhYZUVFNGRHNUNRZ3BRYldSbU5HRk9SMFp2Y2tkaGVrZFlZVVpSVmxCWVowSldVSFlyV1VkSkwwRnFSVUV3VVhwUVF6VmtTRVF2VjFkWVZ6SkhZa1ZETkdSd2QwWnJPRTlIQ2xKcmFVVjRUVTk1THl0RGNXRmlZbFpuS3k5c2VERk9PVlpIUWxSc1ZWUm1kRFExWkFvdExTMHRMVVZPUkNCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2c9PSJ9fX19"}]}, "messageSignature": {"messageDigest": {"algorithm": "SHA2_256", "digest": "gC3WD/iDM4AvJYXnMEO9IcNBKF4Zkv5bMXVeHK3q4w4="}, "signature": "MGUCMQCOOJqTY6XWgB64izK2WVP07b0SG9M5WPCwKhfTPwMvtsgUi8KeRGwQkvvLYbKHdqUCMEbOXFG0NMqEQxWVb6rmGnexdADuGf6Jl8qAC8tn67p3QfVoXzMvFA61PzxwVwvb8g=="}}

This artifact uses snake_case variable names, and as pasted deserializes correctly. This same artifact then fails if any field name is re-named to use camelCase.

@jleightcap
Copy link
Author

jleightcap commented Aug 2, 2023

Okay, I think I might have just went down a rabbit hole based on some incorrect testing setup -- re-generating our schema to consistently use camelCase everywhere seems to (de)serialize just fine. Apologies for the noise -- I believe this was fully user error.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants