selfie of Kenton

Kenton Vizdos

i make cool things in go. this is my dev log. note: it's not a technical masterpiece, I just like writing (sometimes poorly) about what I learn!

Getting the output of govulncheck

This probably isn't production-ready code.

Unless otherwise specified below, this is just a snippet of some lightweight code. The following code is probably too small for a full repo.

Jump to file

This is a small snippet showcasing a possibility of getting data out of govulncheck for usage in Go apps. It was inspired by this Bluesky thread.

For demo purposes, run with GOTOOLCHAIN=go1.24.1 go run main.go to get the http request smuggling vuln.

Take the "not production quality" warning to heart. This is not production quality code, but it's a good starting point for exploring the capabilities of govulncheck.

main.go #

  1package main
  2
  3import (
  4	"bytes"
  5	"context"
  6	"encoding/json"
  7	"fmt"
  8	"log"
  9	"net/http"
 10	"os"
 11	"runtime"
 12
 13	"github.com/golang-jwt/jwt/v5"
 14	"golang.org/x/vuln/scan"
 15)
 16
 17type trace struct {
 18	Module  string `json:"module"`
 19	Package string `json:"package,omitempty"`
 20	Version string `json:"version"`
 21}
 22
 23type Finding struct {
 24	OSV          string  `json:"osv"`
 25	FixedVersion string  `json:"fixed_version"`
 26	Trace        []trace `json:"trace"`
 27}
 28
 29type OSVEntry struct {
 30	SchemaVersion string   `json:"schema_version"`
 31	ID            string   `json:"id"`
 32	Modified      string   `json:"modified"`
 33	Published     string   `json:"published"`
 34	Aliases       []string `json:"aliases"`
 35	Summary       string   `json:"summary"`
 36	Details       string   `json:"details"`
 37	Affected      []struct {
 38		Package struct {
 39			Name      string `json:"name"`
 40			Ecosystem string `json:"ecosystem"`
 41		} `json:"package"`
 42		Ranges []struct {
 43			Type   string `json:"type"`
 44			Events []struct {
 45				Introduced string `json:"introduced,omitempty"`
 46				Fixed      string `json:"fixed,omitempty"`
 47			} `json:"events"`
 48		} `json:"ranges"`
 49		EcosystemSpecific struct {
 50			Imports []struct {
 51				Path    string   `json:"path"`
 52				Symbols []string `json:"symbols"`
 53			} `json:"imports"`
 54		} `json:"ecosystem_specific"`
 55	} `json:"affected"`
 56	References []struct {
 57		Type string `json:"type"`
 58		URL  string `json:"url"`
 59	} `json:"references"`
 60	Credits []struct {
 61		Name string `json:"name"`
 62	} `json:"credits"`
 63	DatabaseSpecific struct {
 64		URL          string `json:"url"`
 65		ReviewStatus string `json:"review_status"`
 66	} `json:"database_specific"`
 67}
 68
 69type wrapper struct {
 70	Finding *Finding  `json:"finding"`
 71	OSV     *OSVEntry `json:"osv"`
 72}
 73
 74func main() {
 75	fmt.Println(runtime.Version())
 76	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
 77		fmt.Fprintln(w, "OK")
 78	})
 79
 80	go func() {
 81		// This uses net/http’s internal chunked encoding parser
 82		// vulnerable to GO-2025-3563 in versions < 1.23.8 and < 1.24.2
 83		err := http.ListenAndServe(":8080", nil)
 84		if err != nil {
 85			panic(err)
 86		}
 87	}()
 88
 89	// demo vuln
 90	_ = jwt.ClaimStrings{}
 91
 92	osvMap := make(map[string]OSVEntry)
 93
 94	var out bytes.Buffer
 95
 96	cmd := scan.Command(context.Background(), "-json", "./...")
 97	cmd.Stdout = &out
 98
 99	err := cmd.Start()
100	if err == nil {
101		err = cmd.Wait()
102	}
103
104	dec := json.NewDecoder(&out)
105	dec.UseNumber()
106
107	for dec.More() {
108		var obj wrapper
109		if err := dec.Decode(&obj); err != nil {
110			fmt.Fprintln(os.Stderr, "failed to decode:", err)
111			continue
112		}
113
114		if obj.OSV != nil {
115			osvMap[obj.OSV.ID] = *obj.OSV
116			continue
117		}
118
119		if obj.Finding == nil {
120			continue
121		}
122
123		mappedVuln, ok := osvMap[obj.Finding.OSV]
124
125		if !ok {
126			log.Println("Couldn't find OSV", obj.Finding.OSV)
127			continue
128		}
129
130		fmt.Println("--- VULN ---")
131		fmt.Printf("Name: %s\n", mappedVuln.Summary)
132		fmt.Printf("Fixed in: %s\n", obj.Finding.FixedVersion)
133		printTrace(obj.Finding.Trace)
134		fmt.Println("--- END VULN ---")
135	}
136
137	switch err := err.(type) {
138	case nil:
139	case interface{ ExitCode() int }:
140		os.Exit(err.ExitCode())
141	default:
142		fmt.Fprintln(os.Stderr, err)
143		os.Exit(1)
144	}
145}
146
147func printTrace(trace []trace) {
148	for i, t := range trace {
149		if t.Package != "" {
150			fmt.Printf("  [%d] %s@%s (%s)\n", i+1, t.Package, t.Version, t.Module)
151		} else {
152			fmt.Printf("  [%d] %s@%s\n", i+1, t.Module, t.Version)
153		}
154	}
155}

go.mod #

 1module github.com/kvizdos/vulncheck-test
 2go 1.24.1
 3toolchain go1.23.0
 4require golang.org/x/vuln v1.1.4
 5require (
 6	github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
 7	golang.org/x/mod v0.22.0 // indirect
 8	golang.org/x/sync v0.10.0 // indirect
 9	golang.org/x/sys v0.29.0 // indirect
10	golang.org/x/telemetry v0.0.0-20240522233618-39ace7a40ae7 // indirect
11	golang.org/x/tools v0.29.0 // indirect
12)