package embed import ( "net" "testing" ) // FuzzEmbed verifies that Embed never panics and that any successful return // keeps the output address inside the requested network. func FuzzEmbed(f *testing.F) { type seed struct { prefix string fields string // comma-separated, mapped below to []Field ns, app string image string fallback string nNibble byte } for _, s := range []seed{ {"2602:817:3000:f001::/64", "namespace,app,image", "mail", "stalwart", "", "ctr", 0xe}, {"2001:db8::/64", "namespace", "ns", "a", "", "", 0}, {"2001:db8::/96", "app", "", "appname", "", "ctr", 0xf}, {"2001:db8::/48", "namespace,app", "ns", "a", "", "ctr", 0x1}, {"2001:db8::/120", "namespace", "n", "a", "", "ctr", 0x0}, // 8 host nibbles {"2001:db8::/124", "namespace", "n", "a", "", "ctr", 0x0}, // 4 host nibbles {"2001:db8::/127", "namespace", "n", "a", "", "ctr", 0x0}, // not nibble-aligned {"2001:db8::/63", "namespace", "n", "a", "", "ctr", 0x0}, // not nibble-aligned {"2001:db8::/64", "namespace,app,image", "", "", "sha256:abcdef0123456789aabbccddeeff00112233445566778899aabbccddeeff0011", "", 0xa}, {"2001:db8::/64", "namespace,app,image", "", "", "traefik:v3.5", "ctr", 0xa}, {"2001:db8::/64", "namespace,app,image", "", "", "", "ctr", 0xa}, {"2001:db8::/64", "namespace", "🦆", "🐧", "", "", 0}, {"2001:db8::/64", "namespace", "ns\x00\x00", "a", "", "", 0}, } { f.Add(s.prefix, s.fields, s.ns, s.app, s.image, s.fallback, s.nNibble) } f.Fuzz(func(t *testing.T, prefix, fieldsStr, ns, app, image, fallback string, nNibble byte) { _, network, err := net.ParseCIDR(prefix) if err != nil { return } fields, ok := decodeFields(fieldsStr) if !ok { return } got, err := Embed(network, fields, Values{ Namespace: ns, App: app, Image: image, ImageFallback: fallback, }, nNibble) if err != nil { return } if !network.Contains(got) { t.Fatalf("Embed(%s, %v) = %s, outside network", prefix, fields, got) } // Property: low nibble of last byte equals nNibble & 0x0F. if want := nNibble & 0x0F; got[len(got)-1]&0x0F != want { t.Fatalf("low nibble = %x, want %x", got[len(got)-1]&0x0F, want) } }) } func decodeFields(s string) ([]Field, bool) { if s == "" { return nil, false } var out []Field cur := []byte{} flush := func() bool { if len(cur) == 0 { return true } switch string(cur) { case string(FieldNamespace): out = append(out, FieldNamespace) case string(FieldApp): out = append(out, FieldApp) case string(FieldImage): out = append(out, FieldImage) default: return false } cur = cur[:0] return true } for i := 0; i < len(s); i++ { if s[i] == ',' { if !flush() { return nil, false } continue } cur = append(cur, s[i]) } if !flush() { return nil, false } if len(out) == 0 { return nil, false } return out, true }