cli/scorecard/proto.go (66 lines of code) (raw):

// Copyright 2019-2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package scorecard import ( "encoding/json" "strconv" "github.com/pkg/errors" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" ) func unMarshallAsset(from []byte, to proto.Message) error { // CAI export returns org_policy [1] with update_time if Timestamp format in Seconds and Nanos // but in protojson, Timestamp is expected to be a string in the RFC 3339 format [2]. // i.e. "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" // Hence doing a workaround to remove the field so that protojson.Unmarshaler can handle org policy. // [1] https://github.com/googleapis/googleapis/blob/master/google/cloud/orgpolicy/v1/orgpolicy.proto // [2] https://godoc.org/google.golang.org/protobuf/types/known/timestamppb#Timestamp // Using json.Unmarshal will return no error // but this approach will lose the "oneof" proto fields in org_policy and access_policy var temp map[string]interface{} err := json.Unmarshal(from, &temp) if err != nil { return errors.Wrap(err, "marshaling to interface") } if val, ok := temp["org_policy"]; ok { for _, op := range val.([]interface{}) { orgPolicy := op.(map[string]interface{}) delete(orgPolicy, "update_time") } } err = protoViaJSON(temp, to) if err == nil { return nil } return err } // protoViaJSON uses JSON as an intermediary serialization to convert a value into // a protobuf message. func protoViaJSON(from interface{}, to proto.Message) error { if m, ok := from.(map[string]interface{}); ok { if val, ok := m["iam_policy"]; ok { if m, ok := val.(map[string]interface{}); ok { delete(m, "etag") } } } jsn, err := json.Marshal(from) if err != nil { return errors.Wrap(err, "marshaling to json") } umar := &protojson.UnmarshalOptions{DiscardUnknown: true} if err := umar.Unmarshal(jsn, to); err != nil { return errors.Wrap(err, "unmarshaling to proto") } return nil } // interfaceViaJSON uses JSON as an intermediary serialization to convert a protobuf message // into an interface value func interfaceViaJSON(from proto.Message) (interface{}, error) { jsn, err := protojson.Marshal(from) if err != nil { return nil, errors.Wrap(err, "marshaling to json") } var to interface{} if err := json.Unmarshal(jsn, &to); err != nil { return nil, errors.Wrap(err, "unmarshaling to interface") } return to, nil } // stringViaJSON uses JSON as an intermediary serialization to convert a protobuf message // into an string value func stringViaJSON(from proto.Message) (string, error) { jsn, err := protojson.Marshal(from) if err != nil { return "", errors.Wrap(err, "marshaling to json") } str, err := strconv.Unquote(string(jsn)) if err != nil { // return original json string if it's not a quoted string return string(jsn), nil } return str, nil }