Skip to content

Instantly share code, notes, and snippets.

@woodruffw
Created May 20, 2026 18:12
Show Gist options
  • Select an option

  • Save woodruffw/2c4a62b2bff3df6cb09cd9b743e9e7a5 to your computer and use it in GitHub Desktop.

Select an option

Save woodruffw/2c4a62b2bff3df6cb09cd9b743e9e7a5 to your computer and use it in GitHub Desktop.
osv repro
// Command reprotest is a standalone reproducer for the empty `{}` OSV event
// regression. It depends only on the serialization libraries (yaml.v2, goccy,
// protojson, osvschema) so it builds without the rest of the vulnfeeds module.
//
// Run with: go run ./reprotest
package main
import (
"fmt"
"strings"
goccyyaml "github.com/goccy/go-yaml"
"github.com/ossf/osv-schema/bindings/go/osvschema"
"google.golang.org/protobuf/encoding/protojson"
"gopkg.in/yaml.v2"
)
// buildVuln constructs a PyJWT-style advisory: affected from version 0 up to and
// including 2.12.1, with no released fix — i.e. an `introduced` + `last_affected`
// pair. This is exactly what AddPkgInfo produces for such an advisory.
func buildVuln() *osvschema.Vulnerability {
return &osvschema.Vulnerability{
Id: "PYSEC-0000-TEST",
Affected: []*osvschema.Affected{
{
Package: &osvschema.Package{
Name: "pyjwt",
Ecosystem: "PyPI",
Purl: "pkg:pypi/pyjwt",
},
Ranges: []*osvschema.Range{
{
Type: osvschema.Range_ECOSYSTEM,
Events: []*osvschema.Event{
{Introduced: "0"},
{LastAffected: "2.12.1"},
},
},
},
},
},
}
}
// oldToYAML reproduces the buggy ToYAML: encode the protobuf struct directly.
func oldToYAML(v *osvschema.Vulnerability) []byte {
out, err := yaml.Marshal(v)
if err != nil {
panic(err)
}
return out
}
// newToYAML reproduces the fixed ToYAML: protojson then JSON->YAML.
func newToYAML(v *osvschema.Vulnerability) []byte {
jsonBytes, err := protojson.Marshal(v)
if err != nil {
panic(err)
}
out, err := goccyyaml.JSONToYAML(jsonBytes)
if err != nil {
panic(err)
}
return out
}
// fromYAML reproduces the current FromYAML: YAML->JSON then protojson decode
// with DiscardUnknown (as introduced in PR #5397).
func fromYAML(data []byte) *osvschema.Vulnerability {
jsonBytes, err := goccyyaml.YAMLToJSON(data)
if err != nil {
panic(err)
}
var v osvschema.Vulnerability
opts := protojson.UnmarshalOptions{DiscardUnknown: true}
if err := opts.Unmarshal(jsonBytes, &v); err != nil {
panic(err)
}
return &v
}
func isEmpty(e *osvschema.Event) bool {
return e.GetIntroduced() == "" && e.GetFixed() == "" &&
e.GetLastAffected() == "" && e.GetLimit() == ""
}
func describeEvents(label string, v *osvschema.Vulnerability) {
fmt.Printf("%s\n", label)
events := v.GetAffected()[0].GetRanges()[0].GetEvents()
emptyCount := 0
for i, e := range events {
if isEmpty(e) {
emptyCount++
}
fmt.Printf(" event[%d]: introduced=%q fixed=%q last_affected=%q limit=%q empty=%v\n",
i, e.GetIntroduced(), e.GetFixed(), e.GetLastAffected(), e.GetLimit(), isEmpty(e))
}
// Re-serialize the decoded proto the way cmd/ids writes final records.
jsonBytes, _ := protojson.Marshal(v)
finalYAML, _ := goccyyaml.JSONToYAML(jsonBytes)
fmt.Printf(" re-serialized record (protojson -> YAML, as cmd/ids writes it):\n")
for _, line := range strings.Split(strings.TrimRight(string(finalYAML), "\n"), "\n") {
fmt.Printf(" %s\n", line)
}
fmt.Printf(" => empty events: %d\n\n", emptyCount)
}
func main() {
v := buildVuln()
fmt.Println("=== BUGGY PATH: old ToYAML (yaml.v2 on the protobuf struct) ===")
buggyYAML := oldToYAML(v)
fmt.Printf("ToYAML output (note the key name):\n%s\n", buggyYAML)
buggyDecoded := fromYAML(buggyYAML)
describeEvents("After FromYAML round-trip:", buggyDecoded)
fmt.Println("=== FIXED PATH: new ToYAML (protojson -> JSONToYAML) ===")
fixedYAML := newToYAML(v)
fmt.Printf("ToYAML output:\n%s\n", fixedYAML)
fixedDecoded := fromYAML(fixedYAML)
describeEvents("After FromYAML round-trip:", fixedDecoded)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment