...

Source file src/google.golang.org/protobuf/reflect/protorange/example_test.go

Documentation: google.golang.org/protobuf/reflect/protorange

     1  // Copyright 2020 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package protorange_test
     6  
     7  import (
     8  	"fmt"
     9  	"strings"
    10  	"time"
    11  
    12  	"google.golang.org/protobuf/encoding/protojson"
    13  	"google.golang.org/protobuf/internal/detrand"
    14  	"google.golang.org/protobuf/proto"
    15  	"google.golang.org/protobuf/reflect/protopath"
    16  	"google.golang.org/protobuf/reflect/protorange"
    17  	"google.golang.org/protobuf/reflect/protoreflect"
    18  	"google.golang.org/protobuf/testing/protopack"
    19  	"google.golang.org/protobuf/types/known/anypb"
    20  	"google.golang.org/protobuf/types/known/timestamppb"
    21  
    22  	newspb "google.golang.org/protobuf/internal/testprotos/news"
    23  )
    24  
    25  func init() {
    26  	detrand.Disable()
    27  }
    28  
    29  func mustMarshal(m proto.Message) []byte {
    30  	b, err := proto.Marshal(m)
    31  	if err != nil {
    32  		panic(err)
    33  	}
    34  	return b
    35  }
    36  
    37  // Range through every message and clear the unknown fields.
    38  func Example_discardUnknown() {
    39  	// Populate the article with unknown fields.
    40  	m := &newspb.Article{}
    41  	m.ProtoReflect().SetUnknown(protopack.Message{
    42  		protopack.Tag{1000, protopack.BytesType}, protopack.String("Hello, world!"),
    43  	}.Marshal())
    44  	fmt.Println("has unknown fields?", len(m.ProtoReflect().GetUnknown()) > 0)
    45  
    46  	// Range through the message and clear all unknown fields.
    47  	fmt.Println("clear unknown fields")
    48  	protorange.Range(m.ProtoReflect(), func(proto protopath.Values) error {
    49  		m, ok := proto.Index(-1).Value.Interface().(protoreflect.Message)
    50  		if ok && len(m.GetUnknown()) > 0 {
    51  			m.SetUnknown(nil)
    52  		}
    53  		return nil
    54  	})
    55  	fmt.Println("has unknown fields?", len(m.ProtoReflect().GetUnknown()) > 0)
    56  
    57  	// Output:
    58  	// has unknown fields? true
    59  	// clear unknown fields
    60  	// has unknown fields? false
    61  }
    62  
    63  // Print the relative paths as Range iterates through a message
    64  // in a depth-first order.
    65  func Example_printPaths() {
    66  	m := &newspb.Article{
    67  		Author:  "Russ Cox",
    68  		Date:    timestamppb.New(time.Date(2019, time.November, 8, 0, 0, 0, 0, time.UTC)),
    69  		Title:   "Go Turns 10",
    70  		Content: "Happy birthday, Go! This weekend we celebrate the 10th anniversary of the Go release...",
    71  		Status:  newspb.Article_PUBLISHED,
    72  		Tags:    []string{"community", "birthday"},
    73  		Attachments: []*anypb.Any{{
    74  			TypeUrl: "google.golang.org.BinaryAttachment",
    75  			Value: mustMarshal(&newspb.BinaryAttachment{
    76  				Name: "gopher-birthday.png",
    77  				Data: []byte("<binary data>"),
    78  			}),
    79  		}},
    80  	}
    81  
    82  	// Traverse over all reachable values and print the path.
    83  	protorange.Range(m.ProtoReflect(), func(p protopath.Values) error {
    84  		fmt.Println(p.Path[1:])
    85  		return nil
    86  	})
    87  
    88  	// Output:
    89  	// .author
    90  	// .date
    91  	// .date.seconds
    92  	// .title
    93  	// .content
    94  	// .status
    95  	// .tags
    96  	// .tags[0]
    97  	// .tags[1]
    98  	// .attachments
    99  	// .attachments[0]
   100  	// .attachments[0].(google.golang.org.BinaryAttachment)
   101  	// .attachments[0].(google.golang.org.BinaryAttachment).name
   102  	// .attachments[0].(google.golang.org.BinaryAttachment).data
   103  }
   104  
   105  // Implement a basic text formatter by ranging through all populated values
   106  // in a message in depth-first order.
   107  func Example_formatText() {
   108  	m := &newspb.Article{
   109  		Author:  "Brad Fitzpatrick",
   110  		Date:    timestamppb.New(time.Date(2018, time.February, 16, 0, 0, 0, 0, time.UTC)),
   111  		Title:   "Go 1.10 is released",
   112  		Content: "Happy Friday, happy weekend! Today the Go team is happy to announce the release of Go 1.10...",
   113  		Status:  newspb.Article_PUBLISHED,
   114  		Tags:    []string{"go1.10", "release"},
   115  		Attachments: []*anypb.Any{{
   116  			TypeUrl: "google.golang.org.KeyValueAttachment",
   117  			Value: mustMarshal(&newspb.KeyValueAttachment{
   118  				Name: "checksums.txt",
   119  				Data: map[string]string{
   120  					"go1.10.src.tar.gz":         "07cbb9d0091b846c6aea40bf5bc0cea7",
   121  					"go1.10.darwin-amd64.pkg":   "cbb38bb6ff6ea86279e01745984445bf",
   122  					"go1.10.linux-amd64.tar.gz": "6b3d0e4a5c77352cf4275573817f7566",
   123  					"go1.10.windows-amd64.msi":  "57bda02030f58f5d2bf71943e1390123",
   124  				},
   125  			}),
   126  		}},
   127  	}
   128  
   129  	// Print a message in a humanly readable format.
   130  	var indent []byte
   131  	protorange.Options{
   132  		Stable: true,
   133  	}.Range(m.ProtoReflect(),
   134  		func(p protopath.Values) error {
   135  			// Print the key.
   136  			var fd protoreflect.FieldDescriptor
   137  			last := p.Index(-1)
   138  			beforeLast := p.Index(-2)
   139  			switch last.Step.Kind() {
   140  			case protopath.FieldAccessStep:
   141  				fd = last.Step.FieldDescriptor()
   142  				fmt.Printf("%s%s: ", indent, fd.Name())
   143  			case protopath.ListIndexStep:
   144  				fd = beforeLast.Step.FieldDescriptor() // lists always appear in the context of a repeated field
   145  				fmt.Printf("%s%d: ", indent, last.Step.ListIndex())
   146  			case protopath.MapIndexStep:
   147  				fd = beforeLast.Step.FieldDescriptor() // maps always appear in the context of a repeated field
   148  				fmt.Printf("%s%v: ", indent, last.Step.MapIndex().Interface())
   149  			case protopath.AnyExpandStep:
   150  				fmt.Printf("%s[%v]: ", indent, last.Value.Message().Descriptor().FullName())
   151  			case protopath.UnknownAccessStep:
   152  				fmt.Printf("%s?: ", indent)
   153  			}
   154  
   155  			// Starting printing the value.
   156  			switch v := last.Value.Interface().(type) {
   157  			case protoreflect.Message:
   158  				fmt.Printf("{\n")
   159  				indent = append(indent, '\t')
   160  			case protoreflect.List:
   161  				fmt.Printf("[\n")
   162  				indent = append(indent, '\t')
   163  			case protoreflect.Map:
   164  				fmt.Printf("{\n")
   165  				indent = append(indent, '\t')
   166  			case protoreflect.EnumNumber:
   167  				var ev protoreflect.EnumValueDescriptor
   168  				if fd != nil {
   169  					ev = fd.Enum().Values().ByNumber(v)
   170  				}
   171  				if ev != nil {
   172  					fmt.Printf("%v\n", ev.Name())
   173  				} else {
   174  					fmt.Printf("%v\n", v)
   175  				}
   176  			case string, []byte:
   177  				fmt.Printf("%q\n", v)
   178  			default:
   179  				fmt.Printf("%v\n", v)
   180  			}
   181  			return nil
   182  		},
   183  		func(p protopath.Values) error {
   184  			// Finish printing the value.
   185  			last := p.Index(-1)
   186  			switch last.Value.Interface().(type) {
   187  			case protoreflect.Message:
   188  				indent = indent[:len(indent)-1]
   189  				fmt.Printf("%s}\n", indent)
   190  			case protoreflect.List:
   191  				indent = indent[:len(indent)-1]
   192  				fmt.Printf("%s]\n", indent)
   193  			case protoreflect.Map:
   194  				indent = indent[:len(indent)-1]
   195  				fmt.Printf("%s}\n", indent)
   196  			}
   197  			return nil
   198  		},
   199  	)
   200  
   201  	// Output:
   202  	// {
   203  	// 	author: "Brad Fitzpatrick"
   204  	// 	date: {
   205  	// 		seconds: 1518739200
   206  	// 	}
   207  	// 	title: "Go 1.10 is released"
   208  	// 	content: "Happy Friday, happy weekend! Today the Go team is happy to announce the release of Go 1.10..."
   209  	// 	attachments: [
   210  	// 		0: {
   211  	// 			[google.golang.org.KeyValueAttachment]: {
   212  	// 				name: "checksums.txt"
   213  	// 				data: {
   214  	//					go1.10.darwin-amd64.pkg: "cbb38bb6ff6ea86279e01745984445bf"
   215  	//					go1.10.linux-amd64.tar.gz: "6b3d0e4a5c77352cf4275573817f7566"
   216  	//					go1.10.src.tar.gz: "07cbb9d0091b846c6aea40bf5bc0cea7"
   217  	//					go1.10.windows-amd64.msi: "57bda02030f58f5d2bf71943e1390123"
   218  	// 				}
   219  	// 			}
   220  	// 		}
   221  	// 	]
   222  	// 	tags: [
   223  	// 		0: "go1.10"
   224  	// 		1: "release"
   225  	// 	]
   226  	// 	status: PUBLISHED
   227  	// }
   228  }
   229  
   230  // Scan all protobuf string values for a sensitive word and replace it with
   231  // a suitable alternative.
   232  func Example_sanitizeStrings() {
   233  	m := &newspb.Article{
   234  		Author:  "Hermione Granger",
   235  		Date:    timestamppb.New(time.Date(1998, time.May, 2, 0, 0, 0, 0, time.UTC)),
   236  		Title:   "Harry Potter vanquishes Voldemort once and for all!",
   237  		Content: "In a final duel between Harry Potter and Lord Voldemort earlier this evening...",
   238  		Tags:    []string{"HarryPotter", "LordVoldemort"},
   239  		Attachments: []*anypb.Any{{
   240  			TypeUrl: "google.golang.org.KeyValueAttachment",
   241  			Value: mustMarshal(&newspb.KeyValueAttachment{
   242  				Name: "aliases.txt",
   243  				Data: map[string]string{
   244  					"Harry Potter": "The Boy Who Lived",
   245  					"Tom Riddle":   "Lord Voldemort",
   246  				},
   247  			}),
   248  		}},
   249  	}
   250  
   251  	protorange.Range(m.ProtoReflect(), func(p protopath.Values) error {
   252  		const (
   253  			sensitive   = "Voldemort"
   254  			alternative = "[He-Who-Must-Not-Be-Named]"
   255  		)
   256  
   257  		// Check if there is a sensitive word to redact.
   258  		last := p.Index(-1)
   259  		s, ok := last.Value.Interface().(string)
   260  		if !ok || !strings.Contains(s, sensitive) {
   261  			return nil
   262  		}
   263  		s = strings.Replace(s, sensitive, alternative, -1)
   264  
   265  		// Store the redacted string back into the message.
   266  		beforeLast := p.Index(-2)
   267  		switch last.Step.Kind() {
   268  		case protopath.FieldAccessStep:
   269  			m := beforeLast.Value.Message()
   270  			fd := last.Step.FieldDescriptor()
   271  			m.Set(fd, protoreflect.ValueOfString(s))
   272  		case protopath.ListIndexStep:
   273  			ls := beforeLast.Value.List()
   274  			i := last.Step.ListIndex()
   275  			ls.Set(i, protoreflect.ValueOfString(s))
   276  		case protopath.MapIndexStep:
   277  			ms := beforeLast.Value.Map()
   278  			k := last.Step.MapIndex()
   279  			ms.Set(k, protoreflect.ValueOfString(s))
   280  		}
   281  		return nil
   282  	})
   283  
   284  	fmt.Println(protojson.Format(m))
   285  
   286  	// Output:
   287  	// {
   288  	//   "author": "Hermione Granger",
   289  	//   "date": "1998-05-02T00:00:00Z",
   290  	//   "title": "Harry Potter vanquishes [He-Who-Must-Not-Be-Named] once and for all!",
   291  	//   "content": "In a final duel between Harry Potter and Lord [He-Who-Must-Not-Be-Named] earlier this evening...",
   292  	//   "tags": [
   293  	//     "HarryPotter",
   294  	//     "Lord[He-Who-Must-Not-Be-Named]"
   295  	//   ],
   296  	//   "attachments": [
   297  	//     {
   298  	//       "@type": "google.golang.org.KeyValueAttachment",
   299  	//       "name": "aliases.txt",
   300  	//       "data": {
   301  	//         "Harry Potter": "The Boy Who Lived",
   302  	//         "Tom Riddle": "Lord [He-Who-Must-Not-Be-Named]"
   303  	//       }
   304  	//     }
   305  	//   ]
   306  	// }
   307  }
   308  

View as plain text