Repository: google/osv-scanner
Pull request #198 (2023-02-08 05:47:28)

govulncheck integration
An initial attempt at integrating govulncheck's library with OSV-Scanner.

TODOs before full PR:

~~- [ ] Add config options after design doc is finalized~~
~~- [ ] Allow user to configure tags and go versions~~
- [x] Update table output for inactive/not called vulnerabilities
- [x] Update exit code for not called vulnerabilities
- [x] Update README with feature and the go dependency if they want the vulncheck feature
- [x] Add go as dependency into osv-scanner docker containers. 

another-rex: Still a draft at the moment, should be ready by end of today though. 

Patch:
diff --git a/.goreleaser.yml b/.goreleaser.yml
index e64d706..bb94f66 100644
--- a/.goreleaser.yml
+++ b/.goreleaser.yml
@@ -83,6 +83,6 @@ checksum:
   name_template: '{{ .ProjectName }}_{{ .Version }}_SHA256SUMS'
   algorithm: sha256
 release:
-  draft: false
+  draft: true
 changelog:
-  skip: true
+  skip: false
diff --git a/cmd/osv-scanner/main.go b/cmd/osv-scanner/main.go
index 32ea2f1..b9a3ea6 100644
--- a/cmd/osv-scanner/main.go
+++ b/cmd/osv-scanner/main.go
@@ -89,6 +89,11 @@ func run(args []string, stdout, stderr io.Writer) int {
 				Usage:   "check subdirectories",
 				Value:   false,
 			},
+			&cli.BoolFlag{
+				Name:  "experimental-call-analysis",
+				Usage: "attempt call analysis on code to detect only active vulnerabilities",
+				Value: false,
+			},
 			&cli.BoolFlag{
 				Name:  "no-ignore",
 				Usage: "also scan files that would be ignored by .gitignore",
@@ -106,14 +111,15 @@ func run(args []string, stdout, stderr io.Writer) int {
 			r = output.NewReporter(stdout, stderr, format)
 
 			vulnResult, err := osvscanner.DoScan(osvscanner.ScannerActions{
-				LockfilePaths:        context.StringSlice("lockfile"),
-				SBOMPaths:            context.StringSlice("sbom"),
-				DockerContainerNames: context.StringSlice("docker"),
-				Recursive:            context.Bool("recursive"),
-				SkipGit:              context.Bool("skip-git"),
-				NoIgnore:             context.Bool("no-ignore"),
-				ConfigOverridePath:   context.String("config"),
-				DirectoryPaths:       context.Args().Slice(),
+				LockfilePaths:            context.StringSlice("lockfile"),
+				SBOMPaths:                context.StringSlice("sbom"),
+				DockerContainerNames:     context.StringSlice("docker"),
+				Recursive:                context.Bool("recursive"),
+				SkipGit:                  context.Bool("skip-git"),
+				NoIgnore:                 context.Bool("no-ignore"),
+				ConfigOverridePath:       context.String("config"),
+				DirectoryPaths:           context.Args().Slice(),
+				ExperimentalCallAnalysis: context.Bool("experimental-call-analysis"),
 			}, r)
 
 			if errPrint := r.PrintResult(&vulnResult); errPrint != nil {
@@ -132,6 +138,11 @@ func run(args []string, stdout, stderr io.Writer) int {
 			return 1
 		}
 
+		if errors.Is(err, osvscanner.OnlyUncalledVulnerabilitiesFoundErr) {
+			// TODO: Discuss whether to have a different exit code now that running call analysis is not default
+			return 2
+		}
+
 		if errors.Is(err, osvscanner.NoPackagesFoundErr) {
 			r.PrintError("No package sources found, --help for usage information.\n")
 			return 128
diff --git a/docs/current/installation.md b/docs/current/installation.md
index 75984a5..09e305f 100644
--- a/docs/current/installation.md
+++ b/docs/current/installation.md
@@ -13,22 +13,37 @@ You may download the [SLSA3](https://slsa.dev) compliant binaries for Linux, mac
 
 [![Packaging status](https://repology.org/badge/vertical-allrepos/osv-scanner.svg)](https://repology.org/project/osv-scanner/versions)
 
-If you're a [**Windows Scoop**](https://scoop.sh) user, then you can install osv-scanner from the [official bucket](https://github.com/ScoopInstaller/Main/blob/master/bucket/osv-scanner.json):
+#### Windows Scoop
+[Windows Scoop](https://scoop.sh) users can install osv-scanner from the [official bucket](https://github.com/ScoopInstaller/Main/blob/master/bucket/osv-scanner.json):
 
 ```bash
 scoop install osv-scanner
 ```
-
-If you're a [Homebrew](https://brew.sh/) user, you can install [osv-scanner](https://formulae.brew.sh/formula/osv-scanner) via:
+#### Homebrew
+[Homebrew](https://brew.sh/) users can install [osv-scanner](https://formulae.brew.sh/formula/osv-scanner) via:
 
 ```bash
 brew install osv-scanner
 ```
 
-If you're a Arch Linux User, you can install osv-scanner from the official repo:
+#### Arch Linux
+Arch Linux users can install osv-scanner from the official repo:
+
 ```bash
 pacman -S osv-scanner
 ```
+#### Alpine Linux
+Alpine Linux users can install osv-scanner from the official repo: 
+
+```bash
+apk add osv-scanner
+```
+#### OpenBSD
+OpenBSD users can install osv-scanner from the official repo:
+
+```bash
+pkg_add osv-scanner
+```
 
 ### Install from source
 
@@ -46,4 +61,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) file.
 
 ### SemVer Adherence
 
-All releases on the same Major version will be guaranteed to have backward compatible JSON output and CLI arguments.
\ No newline at end of file
+All releases on the same Major version will be guaranteed to have backward compatible JSON output and CLI arguments.
diff --git a/docs/working_docs/installation.md b/docs/working_docs/installation.md
index 4b40a2a..64c9dc3 100644
--- a/docs/working_docs/installation.md
+++ b/docs/working_docs/installation.md
@@ -16,22 +16,37 @@ You may download the [SLSA3](https://slsa.dev) compliant binaries for Linux, mac
 
 [![Packaging status](https://repology.org/badge/vertical-allrepos/osv-scanner.svg)](https://repology.org/project/osv-scanner/versions)
 
-If you're a [**Windows Scoop**](https://scoop.sh) user, then you can install osv-scanner from the [official bucket](https://github.com/ScoopInstaller/Main/blob/master/bucket/osv-scanner.json):
+#### Windows Scoop
+[Windows Scoop](https://scoop.sh) users can install osv-scanner from the [official bucket](https://github.com/ScoopInstaller/Main/blob/master/bucket/osv-scanner.json):
 
 ```bash
 scoop install osv-scanner
 ```
-
-If you're a [Homebrew](https://brew.sh/) user, you can install [osv-scanner](https://formulae.brew.sh/formula/osv-scanner) via:
+#### Homebrew
+[Homebrew](https://brew.sh/) users can install [osv-scanner](https://formulae.brew.sh/formula/osv-scanner) via:
 
 ```bash
 brew install osv-scanner
 ```
 
-If you're a Arch Linux User, you can install osv-scanner from the official repo:
+#### Arch Linux
+Arch Linux users can install osv-scanner from the official repo:
+
 ```bash
 pacman -S osv-scanner
 ```
+#### Alpine Linux
+Alpine Linux users can install osv-scanner from the official repo: 
+
+```bash
+apk add osv-scanner
+```
+#### OpenBSD
+OpenBSD users can install osv-scanner from the official repo:
+
+```bash
+pkg_add osv-scanner
+```
 
 ### Install from source
 
@@ -50,3 +65,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) file.
 ### SemVer Adherence
 
 All releases on the same Major version will be guaranteed to have backward compatible JSON output and CLI arguments.
+However, features prefixed with `experimental` (e.g. `--experimental-call-analysis`) might be changed or removed with only a Minor version change.
\ No newline at end of file
diff --git a/docs/working_docs/output.md b/docs/working_docs/output.md
index 454a047..e52513c 100644
--- a/docs/working_docs/output.md
+++ b/docs/working_docs/output.md
@@ -118,7 +118,15 @@ osv-scanner --format json -L path/to/lockfile > /path/to/file.json
               "ids": [
                 "GHSA-c3h9-896r-86jm",
                 "GO-2021-0053"
-              ]
+              ],
+              // Call stack analysis is done using the `--experimental-call-analysis` flag
+              // and result is matched against data provided by the advisory to check if
+              // affected code is actually being executed.
+              "experimentalAnalysis": {
+                "GO-2021-0053": {
+                  "called": false
+                }
+              }
             }
           ]
         }
diff --git a/docs/working_docs/usage.md b/docs/working_docs/usage.md
index 58d4008..c8d689f 100644
--- a/docs/working_docs/usage.md
+++ b/docs/working_docs/usage.md
@@ -99,9 +99,33 @@ it should infer the parser based on the filename:
 osv-scanner --lockfile ':/path/to/my:projects/package-lock.json'
 ```
 
+### Scanning with call analysis
+Preview
+{: .label }
+
+{: .note }
+Features and flags with the `experimental` prefix might change or be removed with only a minor version update.
+
+Call stack analysis can be performed on some languages to check if the 
+vulnerable code is actually being executed by your project. If the code
+is not being executed, these vulnerabilities will be marked as unexecuted.
+
+To enable call analysis, call OSV-Scanner with the `--experimental-call-analysis` flag.
+
+#### Supported languages
+- `go`
+  - Additional dependencies:
+    - `go` compiler needs to be installed and available on PATH
+
+#### Example
+```bash
+osv-scanner --experimental-call-analysis ./my/project/path
+```
+
 ### Scanning a Debian based docker image packages
 Preview
 {: .label } 
+
 This tool will scrape the list of installed packages in a Debian image and query for vulnerabilities on them.
 
 Currently only Debian based docker image scanning is supported.
@@ -135,4 +159,4 @@ appropriate osv-scanner flags:
 
 ```bash
 docker run -it -v ${PWD}:/src ghcr.io/google/osv-scanner -L /src/go.mod
-```
\ No newline at end of file
+```
diff --git a/go.mod b/go.mod
index 2b77148..2755351 100644
--- a/go.mod
+++ b/go.mod
@@ -9,12 +9,15 @@ require (
 	github.com/go-git/go-git/v5 v5.5.2
 	github.com/google/go-cmp v0.5.9
 	github.com/jedib0t/go-pretty/v6 v6.4.4
+	github.com/kr/pretty v0.3.1
 	github.com/package-url/packageurl-go v0.1.0
 	github.com/spdx/tools-golang v0.4.0
 	github.com/urfave/cli/v2 v2.24.4
 	golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb
 	golang.org/x/mod v0.8.0
 	golang.org/x/term v0.5.0
+	golang.org/x/tools v0.5.1-0.20230117180257-8aba49bb5ea2
+	golang.org/x/vuln v0.0.0-20230118164824-4ec8867cc0e6
 	gopkg.in/yaml.v3 v3.0.1
 )
 
@@ -29,13 +32,16 @@ require (
 	github.com/imdario/mergo v0.3.13 // indirect
 	github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
 	github.com/kevinburke/ssh_config v1.2.0 // indirect
+	github.com/kr/text v0.2.0 // indirect
 	github.com/mattn/go-runewidth v0.0.13 // indirect
 	github.com/pjbgf/sha1cd v0.2.3 // indirect
 	github.com/rivo/uniseg v0.2.0 // indirect
+	github.com/rogpeppe/go-internal v1.9.0 // indirect
 	github.com/russross/blackfriday/v2 v2.1.0 // indirect
 	github.com/sergi/go-diff v1.1.0 // indirect
 	github.com/skeema/knownhosts v1.1.0 // indirect
 	github.com/spdx/gordf v0.0.0-20221230105357-b735bd5aac89 // indirect
+	github.com/stretchr/testify v1.8.1 // indirect
 	github.com/xanzy/ssh-agent v0.3.3 // indirect
 	github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
 	golang.org/x/crypto v0.3.0 // indirect
diff --git a/go.sum b/go.sum
index 3195318..509ea39 100644
--- a/go.sum
+++ b/go.sum
@@ -14,6 +14,7 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
 github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M=
 github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
+github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
 github.com/cloudflare/circl v1.1.0 h1:bZgT/A+cikZnKIwn7xL2OBj012Bmvho/o6RpRvv3GKY=
 github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
 github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
@@ -49,8 +50,9 @@ github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c
 github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
 github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
 github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -64,6 +66,7 @@ github.com/package-url/packageurl-go v0.1.0 h1:efWBc98O/dBZRg1pw2xiDzovnlMjCa9NP
 github.com/package-url/packageurl-go v0.1.0/go.mod h1:C/ApiuWpmbpni4DIOECf6WCjFUZV7O1Fx7VAzrZHgBw=
 github.com/pjbgf/sha1cd v0.2.3 h1:uKQP/7QOzNtKYH7UTohZLcjF5/55EnTw0jO/Ru4jZwI=
 github.com/pjbgf/sha1cd v0.2.3/go.mod h1:HOK9QrgzdHpbc2Kzip0Q1yi3M2MFGPADtR6HjG65m5M=
+github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
@@ -71,6 +74,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
+github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
 github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
@@ -85,12 +90,15 @@ github.com/spdx/tools-golang v0.4.0 h1:jdhnW8zYelURCbYTphiviFKZkWu51in0E4A1KT2cs
 github.com/spdx/tools-golang v0.4.0/go.mod h1:VHzvNsKAfAGqs4ZvwRL+7a0dNsL20s7lGui4K9C0xQM=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 github.com/urfave/cli/v2 v2.24.4 h1:0gyJJEBYtCV87zI/x2nZCPyDxD51K6xM8SkwjHFCNEU=
 github.com/urfave/cli/v2 v2.24.4/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
 github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
@@ -120,6 +128,7 @@ golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
 golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -154,6 +163,10 @@ golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.5.1-0.20230117180257-8aba49bb5ea2 h1:v0FhRDmSCNH/0EurAT6T8KRY4aNuUhz6/WwBMxG+gvQ=
+golang.org/x/tools v0.5.1-0.20230117180257-8aba49bb5ea2/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k=
+golang.org/x/vuln v0.0.0-20230118164824-4ec8867cc0e6 h1:XZD8apnMaMVuqE3ZEzf5JJncKMlOsMnnov7U+JRT/d4=
+golang.org/x/vuln v0.0.0-20230118164824-4ec8867cc0e6/go.mod h1:cBP4HMKv0X+x96j8IJWCKk0eqpakBmmHjKGSSC0NaYE=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -170,4 +183,6 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
 gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+honnef.co/go/tools v0.2.2 h1:MNh1AVMyVX23VUHE2O27jm6lNj3vjO5DexS4A1xvnzk=
+mvdan.cc/unparam v0.0.0-20211214103731-d0ef000c54e5 h1:Jh3LAeMt1eGpxomyu3jVkmVZWW2MxZ1qIIV2TZ/nRio=
 sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
diff --git a/goreleaser.dockerfile b/goreleaser.dockerfile
index 8807cd0..c8f4cb6 100644
--- a/goreleaser.dockerfile
+++ b/goreleaser.dockerfile
@@ -12,11 +12,12 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-FROM alpine:latest
+FROM alpine:3.17
 
 RUN apk add --no-cache \
     ca-certificates \
-    git
+    git \
+    go
 
 # Allow git to run on mounted directories
 RUN git config --global --add safe.directory '*'
diff --git a/internal/govulncheckshim/.goignore b/internal/govulncheckshim/.goignore
new file mode 100644
index 0000000..d488960
--- /dev/null
+++ b/internal/govulncheckshim/.goignore
@@ -0,0 +1,1 @@
+fixtures
\ No newline at end of file
diff --git a/internal/govulncheckshim/client.go b/internal/govulncheckshim/client.go
new file mode 100644
index 0000000..d1740d3
--- /dev/null
+++ b/internal/govulncheckshim/client.go
@@ -0,0 +1,121 @@
+package govulncheckshim
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"time"
+
+	"golang.org/x/vuln/client"
+	gvcOSV "golang.org/x/vuln/osv"
+
+	"github.com/google/osv-scanner/pkg/models"
+)
+
+type localSource struct {
+	vulnList         []models.Vulnerability
+	vulnsByID        map[string]*models.Vulnerability
+	vulnsByAlias     map[string][]*models.Vulnerability
+	vulnsByModule    map[string][]*models.Vulnerability
+	lastModifiedTime time.Time
+	client.Client
+}
+
+func newClient(vulns []models.Vulnerability) *localSource {
+	client := localSource{
+		vulnList:         vulns,
+		vulnsByID:        make(map[string]*models.Vulnerability),
+		vulnsByAlias:     make(map[string][]*models.Vulnerability),
+		vulnsByModule:    make(map[string][]*models.Vulnerability),
+		lastModifiedTime: time.Unix(0, 0),
+	}
+	for idx := range vulns {
+		// Iterate on reference to avoid copying entire data structure
+		v := &client.vulnList[idx]
+		client.vulnsByID[v.ID] = v
+		for _, alias := range v.Aliases {
+			client.vulnsByAlias[alias] = append(client.vulnsByAlias[alias], v)
+		}
+		for _, affected := range v.Affected {
+			client.vulnsByModule[affected.Package.Name] = append(client.vulnsByModule[affected.Package.Name], v)
+		}
+		if client.lastModifiedTime.Before(v.Modified) {
+			client.lastModifiedTime = v.Modified
+		}
+	}
+
+	return &client
+}
+
+func convertToGvcOSV(osv models.Vulnerability) (gvcOSV.Entry, error) {
+	val, err := json.Marshal(osv)
+	if err != nil {
+		return gvcOSV.Entry{}, fmt.Errorf("failed to convert vuln to JSON: %w", err)
+	}
+	response := gvcOSV.Entry{}
+	err = json.Unmarshal(val, &response)
+	if err != nil {
+		return gvcOSV.Entry{}, fmt.Errorf("gvc format is no longer compatible with osv format: %w", err)
+	}
+
+	return response, nil
+}
+
+func (ls *localSource) GetByModule(ctx context.Context, modulePath string) ([]*gvcOSV.Entry, error) {
+	//nolint:prealloc // Need to be nil if none exists
+	var entries []*gvcOSV.Entry
+	for _, v := range ls.vulnsByModule[modulePath] {
+		res, err := convertToGvcOSV(*v)
+		if err != nil {
+			return nil, err
+		}
+		entries = append(entries, &res)
+	}
+
+	return entries, nil
+}
+
+func (ls *localSource) GetByID(ctx context.Context, id string) (*gvcOSV.Entry, error) {
+	entry, ok := ls.vulnsByID[id]
+	if !ok {
+		//nolint:nilnil // This follows govulncheck's client implementation
+		// See: https://github.com/golang/vuln/blob/master/client/client.go
+		return nil, nil
+	}
+	response, err := convertToGvcOSV(*entry)
+	if err != nil {
+		return nil, err
+	}
+
+	return &response, nil
+}
+
+func (ls *localSource) GetByAlias(ctx context.Context, alias string) ([]*gvcOSV.Entry, error) {
+	//nolint:prealloc // Need to be nil if none exists
+	var entries []*gvcOSV.Entry
+
+	for _, v := range ls.vulnsByAlias[alias] {
+		res, err := convertToGvcOSV(*v)
+		if err != nil {
+			return nil, err
+		}
+		entries = append(entries, &res)
+	}
+
+	return entries, nil
+}
+
+func (ls *localSource) ListIDs(ctx context.Context) ([]string, error) {
+	//nolint:prealloc // Need to be nil if none exists
+	var ids []string
+	for i := range ls.vulnList {
+		ids = append(ids, ls.vulnList[i].ID)
+	}
+
+	return ids, nil
+}
+
+func (ls *localSource) LastModifiedTime(context.Context) (time.Time, error) {
+	// Assume that if anything changes, the index does.
+	return ls.lastModifiedTime, nil
+}
diff --git a/internal/govulncheckshim/fixtures/GO-2021-0053.json b/internal/govulncheckshim/fixtures/GO-2021-0053.json
new file mode 100644
index 0000000..176ec55
--- /dev/null
+++ b/internal/govulncheckshim/fixtures/GO-2021-0053.json
@@ -0,0 +1,52 @@
+{
+    "id": "GO-2021-0053",
+    "published": "2021-04-14T20:04:52Z",
+    "modified": "2023-02-10T16:51:38Z",
+    "aliases": [
+        "CVE-2021-3121",
+        "GHSA-c3h9-896r-86jm"
+    ],
+    "details": "Due to improper bounds checking, maliciously crafted input to generated Unmarshal methods can cause an out-of-bounds panic. If parsing messages from untrusted parties, this may be used as a denial of service vector.",
+    "affected": [
+        {
+            "package": {
+                "name": "github.com/gogo/protobuf",
+                "ecosystem": "Go"
+            },
+            "ranges": [
+                {
+                    "type": "SEMVER",
+                    "events": [
+                        {
+                            "introduced": "0"
+                        },
+                        {
+                            "fixed": "1.3.2"
+                        }
+                    ]
+                }
+            ],
+            "database_specific": {
+                "url": "https://pkg.go.dev/vuln/GO-2021-0053"
+            },
+            "ecosystem_specific": {
+                "imports": [
+                    {
+                        "path": "github.com/gogo/protobuf/plugin/unmarshal",
+                        "symbols": [
+                            "unmarshal.Generate",
+                            "unmarshal.field"
+                        ]
+                    }
+                ]
+            }
+        }
+    ],
+    "references": [
+        {
+            "type": "FIX",
+            "url": "https://github.com/gogo/protobuf/commit/b03c65ea87cdc3521ede29f62fe3ce239267c1bc"
+        }
+    ],
+    "schema_version": "1.3.1"
+}
\ No newline at end of file
diff --git a/internal/govulncheckshim/fixtures/GO-2023-1558.json b/internal/govulncheckshim/fixtures/GO-2023-1558.json
new file mode 100644
index 0000000..bffcee5
--- /dev/null
+++ b/internal/govulncheckshim/fixtures/GO-2023-1558.json
@@ -0,0 +1,61 @@
+{
+    "id": "GO-2023-1558",
+    "published": "2023-02-14T19:41:21Z",
+    "modified": "2023-02-14T19:41:21Z",
+    "aliases": [
+        "CVE-2023-23626",
+        "GHSA-2h6c-j3gf-xp9r"
+    ],
+    "details": "When feeding untrusted user input into the size parameter of `NewBitfield` and FromBytes functions, an attacker can trigger panics.\n\nThis happens when the size is a not a multiple of 8 or is negative.\n\nA workaround is to ensure size%8 == 0 \u0026\u0026 size \u003e= 0 yourself before calling NewBitfield or FromBytes.",
+    "affected": [
+        {
+            "package": {
+                "name": "github.com/ipfs/go-bitfield",
+                "ecosystem": "Go"
+            },
+            "ranges": [
+                {
+                    "type": "SEMVER",
+                    "events": [
+                        {
+                            "introduced": "0"
+                        },
+                        {
+                            "fixed": "1.1.0"
+                        }
+                    ]
+                }
+            ],
+            "database_specific": {
+                "url": "https://pkg.go.dev/vuln/GO-2023-1558"
+            },
+            "ecosystem_specific": {
+                "imports": [
+                    {
+                        "path": "github.com/ipfs/go-bitfield",
+                        "symbols": [
+                            "FromBytes",
+                            "NewBitfield"
+                        ]
+                    }
+                ]
+            }
+        }
+    ],
+    "references": [
+        {
+            "type": "ADVISORY",
+            "url": "https://github.com/ipfs/go-bitfield/security/advisories/GHSA-2h6c-j3gf-xp9r"
+        },
+        {
+            "type": "FIX",
+            "url": "https://github.com/ipfs/go-bitfield/commit/5e1d256fe043fc4163343ccca83862c69c52e579"
+        }
+    ],
+    "credits": [
+        {
+            "name": "Jorropo"
+        }
+    ],
+    "schema_version": "1.3.1"
+}
\ No newline at end of file
diff --git a/internal/govulncheckshim/fixtures/client/GHSA-cx3w-xqmc-84g5.json b/internal/govulncheckshim/fixtures/client/GHSA-cx3w-xqmc-84g5.json
new file mode 100644
index 0000000..e3b3e89
--- /dev/null
+++ b/internal/govulncheckshim/fixtures/client/GHSA-cx3w-xqmc-84g5.json
@@ -0,0 +1,72 @@
+{
+  "schema_version": "1.3.0",
+  "id": "GHSA-cx3w-xqmc-84g5",
+  "modified": "2022-04-20T16:24:41Z",
+  "published": "2022-02-15T00:30:37Z",
+  "aliases": [
+    "CVE-2021-21237"
+  ],
+  "summary": "Git LFS can execute a Git binary from the current directory on Windows",
+  "details": "### Impact\nOn Windows, if Git LFS operates on a malicious repository with a git.bat or git.exe file in the current directory, that program would be executed, permitting the attacker to execute arbitrary code. This does not affect Unix systems.\n\nThis is the result of an incomplete fix for CVE-2020-27955.\n\nThis issue occurs because on Windows, [Go includes (and prefers) the current directory when the name of a command run does not contain a directory separator](https://github.com/golang/go/issues/38736).\n\n### Patches\nThis version should be patched in v2.13.2, which will be released in coordination with this security advisory.\n\n### Workarounds\nOther than avoiding untrusted repositories or using a different operating system, there is no workaround.\n\n### References\n_Are there any links users can visit to find out more?_\n\n### For more information\nIf you have any questions or comments about this advisory:\n\n- Start a discussion in [the Git LFS discussion page](https://github.com/git-lfs/git-lfs/discussions).\n- If you cannot open a discussion, please email the core team using their usernames at `github.com`.",
+  "severity": [
+    {
+      "type": "CVSS_V3",
+      "score": "CVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:C/C:H/I:H/A:N"
+    }
+  ],
+  "affected": [
+    {
+      "package": {
+        "ecosystem": "Go",
+        "name": "github.com/git-lfs/git-lfs"
+      },
+      "ranges": [
+        {
+          "type": "ECOSYSTEM",
+          "events": [
+            {
+              "introduced": "0"
+            },
+            {
+              "fixed": "2.13.2"
+            }
+          ]
+        }
+      ]
+    }
+  ],
+  "references": [
+    {
+      "type": "WEB",
+      "url": "https://github.com/git-lfs/git-lfs/security/advisories/GHSA-cx3w-xqmc-84g5"
+    },
+    {
+      "type": "ADVISORY",
+      "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-21237"
+    },
+    {
+      "type": "WEB",
+      "url": "https://github.com/git-lfs/git-lfs/commit/fc664697ed2c2081ee9633010de0a7f9debea72a"
+    },
+    {
+      "type": "WEB",
+      "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-27955"
+    },
+    {
+      "type": "PACKAGE",
+      "url": "https://github.com/git-lfs/git-lfs"
+    },
+    {
+      "type": "WEB",
+      "url": "https://github.com/git-lfs/git-lfs/releases/tag/v2.13.2"
+    }
+  ],
+  "database_specific": {
+    "cwe_ids": [
+      "CWE-426",
+      "CWE-94"
+    ],
+    "severity": "HIGH",
+    "github_reviewed": true
+  }
+}
\ No newline at end of file
diff --git a/internal/govulncheckshim/fixtures/client/GHSA-vc3p-29h2-gpcp.json b/internal/govulncheckshim/fixtures/client/GHSA-vc3p-29h2-gpcp.json
new file mode 100644
index 0000000..8182957
--- /dev/null
+++ b/internal/govulncheckshim/fixtures/client/GHSA-vc3p-29h2-gpcp.json
@@ -0,0 +1,85 @@
+{
+  "schema_version": "1.3.0",
+  "id": "GHSA-vc3p-29h2-gpcp",
+  "modified": "2023-02-08T00:37:41Z",
+  "published": "2022-01-02T00:00:48Z",
+  "aliases": [
+    "CVE-2021-44716"
+  ],
+  "summary": "golang.org/x/net/http2 allows uncontrolled memory consumption",
+  "details": "net/http in Go before 1.16.12 and 1.17.x before 1.17.5 allows uncontrolled memory consumption in the header canonicalization cache via HTTP/2 requests.",
+  "severity": [
+    {
+      "type": "CVSS_V3",
+      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H"
+    }
+  ],
+  "affected": [
+    {
+      "package": {
+        "ecosystem": "Go",
+        "name": "golang.org/x/net/http2"
+      },
+      "ranges": [
+        {
+          "type": "ECOSYSTEM",
+          "events": [
+            {
+              "introduced": "0"
+            },
+            {
+              "fixed": "0.0.0-20211209124913-491a49abca63"
+            }
+          ]
+        }
+      ]
+    }
+  ],
+  "references": [
+    {
+      "type": "ADVISORY",
+      "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-44716"
+    },
+    {
+      "type": "WEB",
+      "url": "https://go.dev/cl/369794"
+    },
+    {
+      "type": "WEB",
+      "url": "https://go.dev/issue/50058"
+    },
+    {
+      "type": "WEB",
+      "url": "https://groups.google.com/g/golang-announce/c/hcmEScgc00k"
+    },
+    {
+      "type": "WEB",
+      "url": "https://lists.debian.org/debian-lts-announce/2022/01/msg00016.html"
+    },
+    {
+      "type": "WEB",
+      "url": "https://lists.debian.org/debian-lts-announce/2022/01/msg00017.html"
+    },
+    {
+      "type": "WEB",
+      "url": "https://pkg.go.dev/vuln/GO-2022-0288"
+    },
+    {
+      "type": "WEB",
+      "url": "https://security.gentoo.org/glsa/202208-02"
+    },
+    {
+      "type": "WEB",
+      "url": "https://security.netapp.com/advisory/ntap-20220121-0002/"
+    }
+  ],
+  "database_specific": {
+    "cwe_ids": [
+      "CWE-400"
+    ],
+    "severity": "HIGH",
+    "github_reviewed": true,
+    "github_reviewed_at": "2023-02-08T00:37:41Z",
+    "nvd_published_at": "2022-01-01T05:15:00Z"
+  }
+}
\ No newline at end of file
diff --git a/internal/govulncheckshim/fixtures/client/GO-2021-0098.json b/internal/govulncheckshim/fixtures/client/GO-2021-0098.json
new file mode 100644
index 0000000..9018139
--- /dev/null
+++ b/internal/govulncheckshim/fixtures/client/GO-2021-0098.json
@@ -0,0 +1,107 @@
+{
+    "id": "GO-2021-0098",
+    "published": "2021-04-14T20:04:52Z",
+    "modified": "2023-02-07T16:05:32Z",
+    "aliases": [
+        "CVE-2021-21237",
+        "GHSA-cx3w-xqmc-84g5"
+    ],
+    "details": "Due to the standard library behavior of exec.LookPath on Windows a number of methods may result in arbitrary code execution when cloning or operating on untrusted Git repositories.",
+    "affected": [
+        {
+            "package": {
+                "name": "github.com/git-lfs/git-lfs",
+                "ecosystem": "Go"
+            },
+            "ranges": [
+                {
+                    "type": "SEMVER",
+                    "events": [
+                        {
+                            "introduced": "0"
+                        },
+                        {
+                            "fixed": "1.5.1-0.20210113180018-fc664697ed2c"
+                        }
+                    ]
+                }
+            ],
+            "database_specific": {
+                "url": "https://pkg.go.dev/vuln/GO-2021-0098"
+            },
+            "ecosystem_specific": {
+                "imports": [
+                    {
+                        "path": "github.com/git-lfs/git-lfs/commands",
+                        "goos": [
+                            "windows"
+                        ],
+                        "symbols": [
+                            "PipeCommand",
+                            "PipeMediaCommand",
+                            "Run",
+                            "lockVerifier.Verify",
+                            "singleCheckout.Run",
+                            "singleCheckout.RunToPath",
+                            "uploadContext.NewQueue",
+                            "uploadContext.UploadPointers"
+                        ]
+                    },
+                    {
+                        "path": "github.com/git-lfs/git-lfs/creds",
+                        "goos": [
+                            "windows"
+                        ],
+                        "symbols": [
+                            "AskPassCredentialHelper.Fill",
+                            "AskPassCredentialHelper.getFromProgram",
+                            "CredentialHelperWrapper.FillCreds",
+                            "CredentialHelpers.Approve",
+                            "CredentialHelpers.Fill",
+                            "commandCredentialHelper.Approve"
+                        ]
+                    },
+                    {
+                        "path": "github.com/git-lfs/git-lfs/lfs",
+                        "goos": [
+                            "windows"
+                        ],
+                        "symbols": [
+                            "GitFilter.Clean",
+                            "GitFilter.Smudge",
+                            "GitFilter.SmudgeToFile",
+                            "pipeExtensions"
+                        ]
+                    },
+                    {
+                        "path": "github.com/git-lfs/git-lfs/lfshttp",
+                        "goos": [
+                            "windows"
+                        ],
+                        "symbols": [
+                            "Client.Do",
+                            "Client.DoWithAccess",
+                            "Client.HttpClient",
+                            "Client.NewRequest",
+                            "Client.Transport",
+                            "sshAuthClient.Resolve",
+                            "sshCache.Resolve"
+                        ]
+                    }
+                ]
+            }
+        }
+    ],
+    "references": [
+        {
+            "type": "FIX",
+            "url": "https://github.com/git-lfs/git-lfs/commit/fc664697ed2c2081ee9633010de0a7f9debea72a"
+        }
+    ],
+    "credits": [
+        {
+            "name": "@Ry0taK"
+        }
+    ],
+    "schema_version": "1.3.1"
+}
\ No newline at end of file
diff --git a/internal/govulncheckshim/fixtures/client/GO-2022-0569.json b/internal/govulncheckshim/fixtures/client/GO-2022-0569.json
new file mode 100644
index 0000000..3991535
--- /dev/null
+++ b/internal/govulncheckshim/fixtures/client/GO-2022-0569.json
@@ -0,0 +1,316 @@
+{
+    "id": "GO-2022-0569",
+    "published": "2022-08-23T13:24:17Z",
+    "modified": "2023-02-07T16:05:55Z",
+    "aliases": [
+        "CVE-2022-31836",
+        "GHSA-95f9-94vc-665h"
+    ],
+    "details": "The leafInfo.match() function uses path.join() to deal with wildcard values which can lead to cross directory risk.",
+    "affected": [
+        {
+            "package": {
+                "name": "github.com/beego/beego",
+                "ecosystem": "Go"
+            },
+            "ranges": [
+                {
+                    "type": "SEMVER",
+                    "events": [
+                        {
+                            "introduced": "0"
+                        },
+                        {
+                            "fixed": "1.12.11"
+                        }
+                    ]
+                }
+            ],
+            "database_specific": {
+                "url": "https://pkg.go.dev/vuln/GO-2022-0569"
+            },
+            "ecosystem_specific": {
+                "imports": [
+                    {
+                        "path": "github.com/beego/beego",
+                        "symbols": [
+                            "App.Run",
+                            "ControllerRegister.FindPolicy",
+                            "ControllerRegister.FindRouter",
+                            "ControllerRegister.ServeHTTP",
+                            "FilterRouter.ValidRouter",
+                            "InitBeegoBeforeTest",
+                            "Run",
+                            "RunWithMiddleWares",
+                            "TestBeegoInit",
+                            "Tree.Match",
+                            "adminApp.Run"
+                        ]
+                    }
+                ]
+            }
+        },
+        {
+            "package": {
+                "name": "github.com/beego/beego/v2",
+                "ecosystem": "Go"
+            },
+            "ranges": [
+                {
+                    "type": "SEMVER",
+                    "events": [
+                        {
+                            "introduced": "2.0.0"
+                        },
+                        {
+                            "fixed": "2.0.4"
+                        }
+                    ]
+                }
+            ],
+            "database_specific": {
+                "url": "https://pkg.go.dev/vuln/GO-2022-0569"
+            },
+            "ecosystem_specific": {
+                "imports": [
+                    {
+                        "path": "github.com/beego/beego/v2/server/web",
+                        "symbols": [
+                            "AddNamespace",
+                            "AddViewPath",
+                            "Any",
+                            "AutoPrefix",
+                            "AutoRouter",
+                            "BuildTemplate",
+                            "Compare",
+                            "CompareNot",
+                            "Controller.Abort",
+                            "Controller.Bind",
+                            "Controller.BindForm",
+                            "Controller.BindJSON",
+                            "Controller.BindProtobuf",
+                            "Controller.BindXML",
+                            "Controller.BindYAML",
+                            "Controller.CheckXSRFCookie",
+                            "Controller.CustomAbort",
+                            "Controller.Delete",
+                            "Controller.DestroySession",
+                            "Controller.Get",
+                            "Controller.GetBool",
+                            "Controller.GetFile",
+                            "Controller.GetFloat",
+                            "Controller.GetInt",
+                            "Controller.GetInt16",
+                            "Controller.GetInt32",
+                            "Controller.GetInt64",
+                            "Controller.GetInt8",
+                            "Controller.GetSecureCookie",
+                            "Controller.GetString",
+                            "Controller.GetStrings",
+                            "Controller.GetUint16",
+                            "Controller.GetUint32",
+                            "Controller.GetUint64",
+                            "Controller.GetUint8",
+                            "Controller.Head",
+                            "Controller.Input",
+                            "Controller.IsAjax",
+                            "Controller.JSONResp",
+                            "Controller.Options",
+                            "Controller.ParseForm",
+                            "Controller.Patch",
+                            "Controller.Post",
+                            "Controller.Put",
+                            "Controller.Redirect",
+                            "Controller.Render",
+                            "Controller.RenderBytes",
+                            "Controller.RenderString",
+                            "Controller.Resp",
+                            "Controller.SaveToFile",
+                            "Controller.SaveToFileWithBuffer",
+                            "Controller.ServeFormatted",
+                            "Controller.ServeJSON",
+                            "Controller.ServeJSONP",
+                            "Controller.ServeXML",
+                            "Controller.ServeYAML",
+                            "Controller.SessionRegenerateID",
+                            "Controller.SetData",
+                            "Controller.SetSecureCookie",
+                            "Controller.Trace",
+                            "Controller.URLFor",
+                            "Controller.XMLResp",
+                            "Controller.XSRFFormHTML",
+                            "Controller.XSRFToken",
+                            "Controller.YamlResp",
+                            "ControllerRegister.Add",
+                            "ControllerRegister.AddAuto",
+                            "ControllerRegister.AddAutoPrefix",
+                            "ControllerRegister.AddMethod",
+                            "ControllerRegister.AddRouterMethod",
+                            "ControllerRegister.Any",
+                            "ControllerRegister.CtrlAny",
+                            "ControllerRegister.CtrlDelete",
+                            "ControllerRegister.CtrlGet",
+                            "ControllerRegister.CtrlHead",
+                            "ControllerRegister.CtrlOptions",
+                            "ControllerRegister.CtrlPatch",
+                            "ControllerRegister.CtrlPost",
+                            "ControllerRegister.CtrlPut",
+                            "ControllerRegister.Delete",
+                            "ControllerRegister.FindPolicy",
+                            "ControllerRegister.FindRouter",
+                            "ControllerRegister.Get",
+                            "ControllerRegister.GetContext",
+                            "ControllerRegister.Handler",
+                            "ControllerRegister.Head",
+                            "ControllerRegister.Include",
+                            "ControllerRegister.Init",
+                            "ControllerRegister.InsertFilter",
+                            "ControllerRegister.Options",
+                            "ControllerRegister.Patch",
+                            "ControllerRegister.Post",
+                            "ControllerRegister.Put",
+                            "ControllerRegister.ServeHTTP",
+                            "ControllerRegister.URLFor",
+                            "CtrlAny",
+                            "CtrlDelete",
+                            "CtrlGet",
+                            "CtrlHead",
+                            "CtrlOptions",
+                            "CtrlPatch",
+                            "CtrlPost",
+                            "CtrlPut",
+                            "Date",
+                            "DateFormat",
+                            "DateParse",
+                            "Delete",
+                            "Exception",
+                            "ExecuteTemplate",
+                            "ExecuteViewPathTemplate",
+                            "FileSystem.Open",
+                            "FilterRouter.ValidRouter",
+                            "FlashData.Error",
+                            "FlashData.Notice",
+                            "FlashData.Set",
+                            "FlashData.Store",
+                            "FlashData.Success",
+                            "FlashData.Warning",
+                            "Get",
+                            "GetConfig",
+                            "HTML2str",
+                            "Handler",
+                            "Head",
+                            "Htmlquote",
+                            "Htmlunquote",
+                            "HttpServer.Any",
+                            "HttpServer.AutoPrefix",
+                            "HttpServer.AutoRouter",
+                            "HttpServer.CtrlAny",
+                            "HttpServer.CtrlDelete",
+                            "HttpServer.CtrlGet",
+                            "HttpServer.CtrlHead",
+                            "HttpServer.CtrlOptions",
+                            "HttpServer.CtrlPatch",
+                            "HttpServer.CtrlPost",
+                            "HttpServer.CtrlPut",
+                            "HttpServer.Delete",
+                            "HttpServer.Get",
+                            "HttpServer.Handler",
+                            "HttpServer.Head",
+                            "HttpServer.Include",
+                            "HttpServer.InsertFilter",
+                            "HttpServer.LogAccess",
+                            "HttpServer.Options",
+                            "HttpServer.Patch",
+                            "HttpServer.Post",
+                            "HttpServer.PrintTree",
+                            "HttpServer.Put",
+                            "HttpServer.RESTRouter",
+                            "HttpServer.Router",
+                            "HttpServer.RouterWithOpts",
+                            "HttpServer.Run",
+                            "Include",
+                            "InitBeegoBeforeTest",
+                            "InsertFilter",
+                            "LoadAppConfig",
+                            "LogAccess",
+                            "MapGet",
+                            "Namespace.Any",
+                            "Namespace.AutoPrefix",
+                            "Namespace.AutoRouter",
+                            "Namespace.Cond",
+                            "Namespace.CtrlAny",
+                            "Namespace.CtrlDelete",
+                            "Namespace.CtrlGet",
+                            "Namespace.CtrlHead",
+                            "Namespace.CtrlOptions",
+                            "Namespace.CtrlPatch",
+                            "Namespace.CtrlPost",
+                            "Namespace.CtrlPut",
+                            "Namespace.Delete",
+                            "Namespace.Filter",
+                            "Namespace.Get",
+                            "Namespace.Handler",
+                            "Namespace.Head",
+                            "Namespace.Include",
+                            "Namespace.Namespace",
+                            "Namespace.Options",
+                            "Namespace.Patch",
+                            "Namespace.Post",
+                            "Namespace.Put",
+                            "Namespace.Router",
+                            "NewControllerRegister",
+                            "NewControllerRegisterWithCfg",
+                            "NewHttpServerWithCfg",
+                            "NewHttpSever",
+                            "NewNamespace",
+                            "NotNil",
+                            "Options",
+                            "ParseForm",
+                            "Patch",
+                            "Policy",
+                            "Post",
+                            "PrintTree",
+                            "Put",
+                            "RESTRouter",
+                            "ReadFromRequest",
+                            "RenderForm",
+                            "Router",
+                            "RouterWithOpts",
+                            "Run",
+                            "RunWithMiddleWares",
+                            "TestBeegoInit",
+                            "Tree.AddRouter",
+                            "Tree.AddTree",
+                            "Tree.Match",
+                            "URLFor",
+                            "URLMap.GetMap",
+                            "URLMap.GetMapData",
+                            "Walk",
+                            "adminApp.Run",
+                            "adminController.AdminIndex",
+                            "adminController.Healthcheck",
+                            "adminController.ListConf",
+                            "adminController.ProfIndex",
+                            "adminController.PrometheusMetrics",
+                            "adminController.QpsIndex",
+                            "adminController.TaskStatus",
+                            "beegoAppConfig.Bool",
+                            "beegoAppConfig.DefaultBool"
+                        ]
+                    }
+                ]
+            }
+        }
+    ],
+    "references": [
+        {
+            "type": "FIX",
+            "url": "https://github.com/beego/beego/pull/5025"
+        },
+        {
+            "type": "FIX",
+            "url": "https://github.com/beego/beego/pull/5025/commits/ea5ae58d40589d249cf577a053e490509de2bf57"
+        }
+    ],
+    "schema_version": "1.3.1"
+}
\ No newline at end of file
diff --git a/internal/govulncheckshim/govulncheckshim.go b/internal/govulncheckshim/govulncheckshim.go
new file mode 100644
index 0000000..9f512c3
--- /dev/null
+++ b/internal/govulncheckshim/govulncheckshim.go
@@ -0,0 +1,80 @@
+package govulncheckshim
+
+import (
+	"context"
+	"fmt"
+	"path/filepath"
+	"strings"
+
+	"golang.org/x/tools/go/packages"
+	"golang.org/x/vuln/exp/govulncheck"
+	"golang.org/x/vuln/vulncheck"
+
+	"github.com/google/osv-scanner/pkg/models"
+)
+
+type packageError struct {
+	Errors []packages.Error
+}
+
+func (e *packageError) Error() string {
+	var b strings.Builder
+	fmt.Fprintln(&b, "Packages contain errors:")
+	for _, e := range e.Errors {
+		fmt.Fprintln(&b, e)
+	}
+
+	return b.String()
+}
+
+// RunGoVulnCheck runs govulncheck with a subset of vulnerabilities identified by osv-scanner
+func RunGoVulnCheck(path string, vulns []models.Vulnerability) (*govulncheck.Result, error) {
+	path, err := filepath.Abs(path)
+	if err != nil {
+		return nil, err
+	}
+	scanPath := filepath.Join(path, "...")
+	client := newClient(vulns)
+
+	cfg := &govulncheck.Config{Client: client}
+	pkgs, err := loadPackages([]string{scanPath}, filepath.Dir(scanPath))
+	if err != nil {
+		return nil, err
+	}
+
+	return govulncheck.Source(context.Background(), cfg, pkgs)
+}
+
+// loadPackages loads the packages matching patterns at dir using build tags
+// provided by tagsFlag. Uses load mode needed for vulncheck analysis. If the
+// packages contain errors, a packageError is returned containing a list of
+// the errors, along with the packages themselves.
+func loadPackages(patterns []string, dir string) ([]*vulncheck.Package, error) {
+	var buildFlags []string
+	// TODO: Enable user configurable tags
+	// if tagsFlag != nil {
+	// 	buildFlags = []string{fmt.Sprintf("-tags=%s", strings.Join(tagsFlag, ","))}
+	// }
+
+	cfg := &packages.Config{Dir: dir, Tests: true}
+	cfg.Mode |= packages.NeedName | packages.NeedImports | packages.NeedTypes |
+		packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedDeps |
+		packages.NeedModule
+	cfg.BuildFlags = buildFlags
+
+	pkgs, err := packages.Load(cfg, patterns...)
+	if err != nil {
+		return nil, err
+	}
+	vpkgs := vulncheck.Convert(pkgs)
+
+	var perrs []packages.Error
+	packages.Visit(pkgs, nil, func(p *packages.Package) {
+		perrs = append(perrs, p.Errors...)
+	})
+	if len(perrs) > 0 {
+		err = &packageError{perrs}
+	}
+
+	return vpkgs, err
+}
diff --git a/internal/output/table.go b/internal/output/table.go
index b42c9a1..c363a0b 100644
--- a/internal/output/table.go
+++ b/internal/output/table.go
@@ -19,7 +19,6 @@ func PrintTableResults(vulnResult *models.VulnerabilityResults, outputWriter io.
 	outputTable := table.NewWriter()
 	outputTable.SetOutputMirror(outputWriter)
 	outputTable.AppendHeader(table.Row{"OSV URL (ID In Bold)", "Ecosystem", "Package", "Version", "Source"})
-
 	width, _, err := term.GetSize(int(os.Stdout.Fd()))
 	isTerminal := false
 	if err == nil { // If output is a terminal, set max length to width and add styling
@@ -40,6 +39,34 @@ func PrintTableResults(vulnResult *models.VulnerabilityResults, outputWriter io.
 }
 
 func tableBuilder(outputTable table.Writer, vulnResult *models.VulnerabilityResults, addStyling bool) table.Writer {
+	rows := tableBuilderInner(vulnResult, addStyling, true)
+	for _, elem := range rows {
+		outputTable.AppendRow(elem.row, table.RowConfig{AutoMerge: elem.shouldMerge})
+	}
+
+	uncalledRows := tableBuilderInner(vulnResult, addStyling, false)
+	if len(uncalledRows) == 0 {
+		return outputTable
+	}
+
+	outputTable.AppendSeparator()
+	outputTable.AppendRow(table.Row{"Uncalled vulnerabilities"})
+	outputTable.AppendSeparator()
+
+	for _, elem := range uncalledRows {
+		outputTable.AppendRow(elem.row, table.RowConfig{AutoMerge: elem.shouldMerge})
+	}
+
+	return outputTable
+}
+
+type tbInnerResponse struct {
+	row         table.Row
+	shouldMerge bool
+}
+
+func tableBuilderInner(vulnResult *models.VulnerabilityResults, addStyling bool, calledVulns bool) []tbInnerResponse {
+	allOutputRows := []tbInnerResponse{}
 	// Working directory used to simplify path
 	workingDir, workingDirErr := os.Getwd()
 	for _, sourceRes := range vulnResult.Results {
@@ -54,6 +81,10 @@ func tableBuilder(outputTable table.Writer, vulnResult *models.VulnerabilityResu
 
 			// Merge groups into the same row
 			for _, group := range pkg.Groups {
+				if group.IsCalled() != calledVulns {
+					continue
+				}
+
 				outputRow := table.Row{}
 				shouldMerge := false
 
@@ -77,10 +108,13 @@ func tableBuilder(outputTable table.Writer, vulnResult *models.VulnerabilityResu
 				}
 
 				outputRow = append(outputRow, source.Path)
-				outputTable.AppendRow(outputRow, table.RowConfig{AutoMerge: shouldMerge})
+				allOutputRows = append(allOutputRows, tbInnerResponse{
+					row:         outputRow,
+					shouldMerge: shouldMerge,
+				})
 			}
 		}
 	}
 
-	return outputTable
+	return allOutputRows
 }
diff --git a/internal/sourceanalysis/fixtures/govulncheckinput.json b/internal/sourceanalysis/fixtures/govulncheckinput.json
new file mode 100644
index 0000000..e70cca4
--- /dev/null
+++ b/internal/sourceanalysis/fixtures/govulncheckinput.json
@@ -0,0 +1,169 @@
+{
+  "GO-2021-0053": {
+    "OSV": {
+      "id": "GO-2021-0053",
+      "published": "2021-04-14T20:04:52Z",
+      "modified": "2023-02-10T16:51:38Z",
+      "aliases": [
+        "CVE-2021-3121",
+        "GHSA-c3h9-896r-86jm"
+      ],
+      "details": "Due to improper bounds checking, maliciously crafted input to generated Unmarshal methods can cause an out-of-bounds panic. If parsing messages from untrusted parties, this may be used as a denial of service vector.",
+      "affected": [
+        {
+          "package": {
+            "name": "github.com/gogo/protobuf",
+            "ecosystem": "Go"
+          },
+          "ranges": [
+            {
+              "type": "SEMVER",
+              "events": [
+                {
+                  "introduced": "0"
+                },
+                {
+                  "fixed": "1.3.2"
+                }
+              ]
+            }
+          ],
+          "database_specific": {
+            "url": "https://pkg.go.dev/vuln/GO-2021-0053"
+          },
+          "ecosystem_specific": {
+            "imports": [
+              {
+                "path": "github.com/gogo/protobuf/plugin/unmarshal",
+                "symbols": [
+                  "unmarshal.Generate",
+                  "unmarshal.field"
+                ]
+              }
+            ]
+          }
+        }
+      ],
+      "references": [
+        {
+          "type": "FIX",
+          "url": "https://github.com/gogo/protobuf/commit/b03c65ea87cdc3521ede29f62fe3ce239267c1bc"
+        }
+      ],
+      "schema_version": "1.3.0"
+    },
+    "Modules": [
+      {
+        "Path": "github.com/gogo/protobuf",
+        "FoundVersion": "v1.3.1",
+        "FixedVersion": "v1.3.2",
+        "Packages": [
+          {
+            "Path": "github.com/gogo/protobuf/plugin/unmarshal",
+            "CallStacks": null
+          }
+        ]
+      }
+    ]
+  },
+  "GO-2023-1558": {
+    "OSV": {
+      "id": "GO-2023-1558",
+      "published": "2023-02-14T19:41:21Z",
+      "modified": "2023-02-14T19:41:21Z",
+      "aliases": [
+        "CVE-2023-23626",
+        "GHSA-2h6c-j3gf-xp9r"
+      ],
+      "details": "When feeding untrusted user input into the size parameter of `NewBitfield` and FromBytes functions, an attacker can trigger panics.\n\nThis happens when the size is a not a multiple of 8 or is negative.\n\nA workaround is to ensure size%8 == 0 \u0026\u0026 size \u003e= 0 yourself before calling NewBitfield or FromBytes.",
+      "affected": [
+        {
+          "package": {
+            "name": "github.com/ipfs/go-bitfield",
+            "ecosystem": "Go"
+          },
+          "ranges": [
+            {
+              "type": "SEMVER",
+              "events": [
+                {
+                  "introduced": "0"
+                },
+                {
+                  "fixed": "1.1.0"
+                }
+              ]
+            }
+          ],
+          "database_specific": {
+            "url": "https://pkg.go.dev/vuln/GO-2023-1558"
+          },
+          "ecosystem_specific": {
+            "imports": [
+              {
+                "path": "github.com/ipfs/go-bitfield",
+                "symbols": [
+                  "FromBytes",
+                  "NewBitfield"
+                ]
+              }
+            ]
+          }
+        }
+      ],
+      "references": [
+        {
+          "type": "ADVISORY",
+          "url": "https://github.com/ipfs/go-bitfield/security/advisories/GHSA-2h6c-j3gf-xp9r"
+        },
+        {
+          "type": "FIX",
+          "url": "https://github.com/ipfs/go-bitfield/commit/5e1d256fe043fc4163343ccca83862c69c52e579"
+        }
+      ],
+      "schema_version": "1.3.0"
+    },
+    "Modules": [
+      {
+        "Path": "github.com/ipfs/go-bitfield",
+        "FoundVersion": "v1.0.0",
+        "FixedVersion": "v1.1.0",
+        "Packages": [
+          {
+            "Path": "github.com/ipfs/go-bitfield",
+            "CallStacks": [
+              {
+                "Symbol": "NewBitfield",
+                "Summary": "../../internal/govulncheckshim/fixtures/test-project/main.go:13:22: github.com/ossf-tests/osv-e2e.main calls github.com/ipfs/go-bitfield.NewBitfield",
+                "Frames": [
+                  {
+                    "PkgPath": "github.com/ossf-tests/osv-e2e",
+                    "FuncName": "main",
+                    "RecvType": "",
+                    "Position": {
+                      "Filename": "/home/rexpan/Documents/Projects/osv-scanner/internal/govulncheckshim/fixtures/test-project/main.go",
+                      "Offset": 234,
+                      "Line": 13,
+                      "Column": 22
+                    }
+                  },
+                  {
+                    "PkgPath": "github.com/ipfs/go-bitfield",
+                    "FuncName": "NewBitfield",
+                    "RecvType": "",
+                    "Position": {
+                      "Filename": "",
+                      "Offset": 0,
+                      "Line": 0,
+                      "Column": 0
+                    }
+                  }
+                ]
+              }
+            ]
+          }
+        ]
+      }
+    ]
+  }
+}
\ No newline at end of file
diff --git a/internal/sourceanalysis/fixtures/input.json b/internal/sourceanalysis/fixtures/input.json
new file mode 100644
index 0000000..3f64759
--- /dev/null
+++ b/internal/sourceanalysis/fixtures/input.json
@@ -0,0 +1,457 @@
+[
+  {
+    "package": {
+      "name": "github.com/gogo/protobuf",
+      "version": "1.3.1",
+      "ecosystem": "Go"
+    },
+    "vulnerabilities": [
+      {
+        "schema_version": "1.3.0",
+        "id": "GHSA-c3h9-896r-86jm",
+        "modified": "2022-03-28T20:28:00Z",
+        "published": "2022-03-28T20:28:00Z",
+        "aliases": [
+          "CVE-2021-3121"
+        ],
+        "summary": "Improper Input Validation in GoGo Protobuf",
+        "details": "An issue was discovered in GoGo Protobuf before 1.3.2. plugin/unmarshal/unmarshal.go lacks certain index validation, aka the \"skippy peanut butter\" issue.",
+        "affected": [
+          {
+            "package": {
+              "ecosystem": "Go",
+              "name": "github.com/gogo/protobuf",
+              "purl": "pkg:golang/github.com/gogo/protobuf"
+            },
+            "ranges": [
+              {
+                "type": "SEMVER",
+                "events": [
+                  {
+                    "introduced": "0"
+                  },
+                  {
+                    "fixed": "1.3.2"
+                  }
+                ]
+              }
+            ],
+            "database_specific": {
+              "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2022/03/GHSA-c3h9-896r-86jm/GHSA-c3h9-896r-86jm.json"
+            }
+          }
+        ],
+        "references": [
+          {
+            "type": "ADVISORY",
+            "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-3121"
+          },
+          {
+            "type": "WEB",
+            "url": "https://github.com/gogo/protobuf/commit/b03c65ea87cdc3521ede29f62fe3ce239267c1bc"
+          },
+          {
+            "type": "WEB",
+            "url": "https://discuss.hashicorp.com/t/hcsec-2021-23-consul-exposed-to-denial-of-service-in-gogo-protobuf-dependency/29025"
+          },
+          {
+            "type": "PACKAGE",
+            "url": "https://github.com/gogo/protobuf"
+          },
+          {
+            "type": "WEB",
+            "url": "https://github.com/gogo/protobuf/compare/v1.3.1...v1.3.2"
+          },
+          {
+            "type": "WEB",
+            "url": "https://lists.apache.org/thread.html/r68032132c0399c29d6cdc7bd44918535da54060a10a12b1591328bff@%3Cnotifications.skywalking.apache.org%3E"
+          },
+          {
+            "type": "WEB",
+            "url": "https://lists.apache.org/thread.html/r88d69555cb74a129a7bf84838073b61259b4a3830190e05a3b87994e@%3Ccommits.pulsar.apache.org%3E"
+          },
+          {
+            "type": "WEB",
+            "url": "https://lists.apache.org/thread.html/rc1e9ff22c5641d73701ba56362fb867d40ed287cca000b131dcf4a44@%3Ccommits.pulsar.apache.org%3E"
+          },
+          {
+            "type": "WEB",
+            "url": "https://pkg.go.dev/vuln/GO-2021-0053"
+          },
+          {
+            "type": "WEB",
+            "url": "https://security.netapp.com/advisory/ntap-20210219-0006/"
+          }
+        ],
+        "database_specific": {
+          "cwe_ids": [
+            "CWE-129",
+            "CWE-20"
+          ],
+          "github_reviewed": true,
+          "github_reviewed_at": "2022-03-28T20:28:00Z",
+          "nvd_published_at": "2021-01-11T06:15:00Z",
+          "severity": "HIGH"
+        }
+      },
+      {
+        "schema_version": "1.3.0",
+        "id": "GO-2021-0053",
+        "modified": "2023-02-10T16:51:38Z",
+        "published": "2021-04-14T20:04:52Z",
+        "aliases": [
+          "CVE-2021-3121",
+          "GHSA-c3h9-896r-86jm"
+        ],
+        "summary": "",
+        "details": "Due to improper bounds checking, maliciously crafted input to generated Unmarshal methods can cause an out-of-bounds panic. If parsing messages from untrusted parties, this may be used as a denial of service vector.",
+        "affected": [
+          {
+            "package": {
+              "ecosystem": "Go",
+              "name": "github.com/gogo/protobuf",
+              "purl": "pkg:golang/github.com/gogo/protobuf"
+            },
+            "ranges": [
+              {
+                "type": "SEMVER",
+                "events": [
+                  {
+                    "introduced": "0"
+                  },
+                  {
+                    "fixed": "1.3.2"
+                  }
+                ]
+              }
+            ],
+            "database_specific": {
+              "source": "https://vuln.go.dev/ID/GO-2021-0053.json",
+              "url": "https://pkg.go.dev/vuln/GO-2021-0053"
+            },
+            "ecosystem_specific": {
+              "imports": [
+                {
+                  "path": "github.com/gogo/protobuf/plugin/unmarshal",
+                  "symbols": [
+                    "unmarshal.Generate",
+                    "unmarshal.field"
+                  ]
+                }
+              ]
+            }
+          }
+        ],
+        "references": [
+          {
+            "type": "FIX",
+            "url": "https://github.com/gogo/protobuf/commit/b03c65ea87cdc3521ede29f62fe3ce239267c1bc"
+          }
+        ]
+      }
+    ],
+    "groups": [
+      {
+        "ids": [
+          "GHSA-c3h9-896r-86jm",
+          "GO-2021-0053"
+        ]
+      }
+    ]
+  },
+  {
+    "package": {
+      "name": "github.com/ipfs/go-bitfield",
+      "version": "1.0.0",
+      "ecosystem": "Go"
+    },
+    "vulnerabilities": [
+      {
+        "schema_version": "1.3.0",
+        "id": "GHSA-2h6c-j3gf-xp9r",
+        "modified": "2023-02-10T19:52:45Z",
+        "published": "2023-02-10T19:52:45Z",
+        "aliases": [
+          "CVE-2023-23626"
+        ],
+        "summary": "IPFS go-bitfield vulnerable to DoS via malformed size arguments",
+        "details": "### Impact\nWhen feeding untrusted user input into the size parameter of `NewBitfield` and `FromBytes` functions, an attacker can trigger `panic`s.\n\nThis happen when the `size` is a not a multiple of `8` or is negative.\nThere were already a note in the `NewBitfield` documentation:\n\u003e ```\n\u003e Panics if size is not a multiple of 8.\n\u003e ````\n\nBut it incomplete and missing from `FromBytes`'s documentation.\n\nThis has been replaced by returning an `(Bitfield, error)` and returning a non nil error if the size is wrong.\n\n### Patches\n- https://github.com/ipfs/go-bitfield/commit/5e1d256fe043fc4163343ccca83862c69c52e579\n\n### Workarounds\n- Ensure `size%8 == 0 \u0026\u0026 size \u003e= 0` yourself before calling `NewBitfield` or `FromBytes`\n\n### References\n- https://github.com/ipfs/go-unixfs/security/advisories/GHSA-q264-w97q-q778\n",
+        "affected": [
+          {
+            "package": {
+              "ecosystem": "Go",
+              "name": "github.com/ipfs/go-bitfield",
+              "purl": "pkg:golang/github.com/ipfs/go-bitfield"
+            },
+            "ranges": [
+              {
+                "type": "SEMVER",
+                "events": [
+                  {
+                    "introduced": "1.0.0"
+                  },
+                  {
+                    "fixed": "1.1.0"
+                  }
+                ]
+              }
+            ],
+            "versions": [
+              "1.0.0"
+            ],
+            "database_specific": {
+              "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2023/02/GHSA-2h6c-j3gf-xp9r/GHSA-2h6c-j3gf-xp9r.json"
+            }
+          }
+        ],
+        "references": [
+          {
+            "type": "WEB",
+            "url": "https://github.com/ipfs/go-bitfield/security/advisories/GHSA-2h6c-j3gf-xp9r"
+          },
+          {
+            "type": "ADVISORY",
+            "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-23626"
+          },
+          {
+            "type": "WEB",
+            "url": "https://github.com/ipfs/go-bitfield/commit/5e1d256fe043fc4163343ccca83862c69c52e579"
+          },
+          {
+            "type": "PACKAGE",
+            "url": "https://github.com/ipfs/go-bitfield"
+          },
+          {
+            "type": "WEB",
+            "url": "https://pkg.go.dev/vuln/GO-2023-1558"
+          }
+        ],
+        "database_specific": {
+          "cwe_ids": [
+            "CWE-1284",
+            "CWE-754"
+          ],
+          "github_reviewed": true,
+          "github_reviewed_at": "2023-02-10T19:52:45Z",
+          "nvd_published_at": "2023-02-09T21:15:00Z",
+          "severity": "MODERATE"
+        }
+      },
+      {
+        "schema_version": "1.3.0",
+        "id": "GO-2023-1558",
+        "modified": "2023-02-14T19:41:21Z",
+        "published": "2023-02-14T19:41:21Z",
+        "aliases": [
+          "CVE-2023-23626",
+          "GHSA-2h6c-j3gf-xp9r"
+        ],
+        "summary": "",
+        "details": "When feeding untrusted user input into the size parameter of `NewBitfield` and FromBytes functions, an attacker can trigger panics.\n\nThis happens when the size is a not a multiple of 8 or is negative.\n\nA workaround is to ensure size%8 == 0 \u0026\u0026 size \u003e= 0 yourself before calling NewBitfield or FromBytes.",
+        "affected": [
+          {
+            "package": {
+              "ecosystem": "Go",
+              "name": "github.com/ipfs/go-bitfield",
+              "purl": "pkg:golang/github.com/ipfs/go-bitfield"
+            },
+            "ranges": [
+              {
+                "type": "SEMVER",
+                "events": [
+                  {
+                    "introduced": "0"
+                  },
+                  {
+                    "fixed": "1.1.0"
+                  }
+                ]
+              }
+            ],
+            "database_specific": {
+              "source": "https://vuln.go.dev/ID/GO-2023-1558.json",
+              "url": "https://pkg.go.dev/vuln/GO-2023-1558"
+            },
+            "ecosystem_specific": {
+              "imports": [
+                {
+                  "path": "github.com/ipfs/go-bitfield",
+                  "symbols": [
+                    "FromBytes",
+                    "NewBitfield"
+                  ]
+                }
+              ]
+            }
+          }
+        ],
+        "references": [
+          {
+            "type": "ADVISORY",
+            "url": "https://github.com/ipfs/go-bitfield/security/advisories/GHSA-2h6c-j3gf-xp9r"
+          },
+          {
+            "type": "FIX",
+            "url": "https://github.com/ipfs/go-bitfield/commit/5e1d256fe043fc4163343ccca83862c69c52e579"
+          }
+        ]
+      }
+    ],
+    "groups": [
+      {
+        "ids": [
+          "GHSA-2h6c-j3gf-xp9r",
+          "GO-2023-1558"
+        ]
+      }
+    ]
+  },
+  {
+    "package": {
+      "name": "golang.org/x/image",
+      "version": "0.4.0",
+      "ecosystem": "Go"
+    },
+    "vulnerabilities": [
+      {
+        "schema_version": "1.3.0",
+        "id": "GHSA-qgc7-mgm3-q253",
+        "modified": "2023-02-17T13:59:44Z",
+        "published": "2023-02-17T13:59:44Z",
+        "aliases": [
+          "CVE-2022-41727"
+        ],
+        "summary": "Uncontrolled Resource Consumption",
+        "details": "An attacker can craft a malformed TIFF image which will consume a significant amount of memory when passed to DecodeConfig. This could lead to a denial of service.",
+        "affected": [
+          {
+            "package": {
+              "ecosystem": "Go",
+              "name": "golang.org/x/image",
+              "purl": "pkg:golang/golang.org/x/image"
+            },
+            "ranges": [
+              {
+                "type": "SEMVER",
+                "events": [
+                  {
+                    "introduced": "0"
+                  },
+                  {
+                    "fixed": "0.5.0"
+                  }
+                ]
+              }
+            ],
+            "database_specific": {
+              "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2023/02/GHSA-qgc7-mgm3-q253/GHSA-qgc7-mgm3-q253.json"
+            }
+          }
+        ],
+        "references": [
+          {
+            "type": "ADVISORY",
+            "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-41727"
+          },
+          {
+            "type": "WEB",
+            "url": "https://go.dev/cl/468195"
+          },
+          {
+            "type": "WEB",
+            "url": "https://go.dev/issue/58003"
+          },
+          {
+            "type": "WEB",
+            "url": "https://groups.google.com/g/golang-announce/c/ag-FiyjlD5o"
+          },
+          {
+            "type": "WEB",
+            "url": "https://pkg.go.dev/vuln/GO-2023-1572"
+          }
+        ],
+        "database_specific": {
+          "cwe_ids": [
+            "CWE-400"
+          ],
+          "github_reviewed": true,
+          "github_reviewed_at": "2023-02-17T13:59:44Z",
+          "nvd_published_at": null,
+          "severity": "LOW"
+        }
+      },
+      {
+        "schema_version": "1.3.0",
+        "id": "GO-2023-1572",
+        "modified": "2023-02-16T22:25:24Z",
+        "published": "2023-02-16T22:25:24Z",
+        "aliases": [
+          "CVE-2022-41727"
+        ],
+        "summary": "",
+        "details": "An attacker can craft a malformed TIFF image which will consume a significant amount of memory when passed to DecodeConfig. This could lead to a denial of service.",
+        "affected": [
+          {
+            "package": {
+              "ecosystem": "Go",
+              "name": "golang.org/x/image",
+              "purl": "pkg:golang/golang.org/x/image"
+            },
+            "ranges": [
+              {
+                "type": "SEMVER",
+                "events": [
+                  {
+                    "introduced": "0"
+                  },
+                  {
+                    "fixed": "0.5.0"
+                  }
+                ]
+              }
+            ],
+            "database_specific": {
+              "source": "https://vuln.go.dev/ID/GO-2023-1572.json",
+              "url": "https://pkg.go.dev/vuln/GO-2023-1572"
+            },
+            "ecosystem_specific": {
+              "imports": [
+                {
+                  "path": "golang.org/x/image/tiff",
+                  "symbols": [
+                    "Decode",
+                    "DecodeConfig",
+                    "decoder.ifdUint",
+                    "newDecoder"
+                  ]
+                }
+              ]
+            }
+          }
+        ],
+        "references": [
+          {
+            "type": "REPORT",
+            "url": "https://go.dev/issue/58003"
+          },
+          {
+            "type": "FIX",
+            "url": "https://go.dev/cl/468195"
+          },
+          {
+            "type": "WEB",
+            "url": "https://groups.google.com/g/golang-announce/c/ag-FiyjlD5o"
+          }
+        ]
+      }
+    ],
+    "groups": [
+      {
+        "ids": [
+          "GHSA-qgc7-mgm3-q253",
+          "GO-2023-1572"
+        ]
+      }
+    ]
+  }
+]
\ No newline at end of file
diff --git a/internal/sourceanalysis/fixtures/output.json b/internal/sourceanalysis/fixtures/output.json
new file mode 100644
index 0000000..0b8eb66
--- /dev/null
+++ b/internal/sourceanalysis/fixtures/output.json
@@ -0,0 +1,472 @@
+[
+  {
+    "groups": [
+      {
+        "experimentalAnalysis": {
+          "GO-2021-0053": {
+            "called": false
+          }
+        },
+        "ids": [
+          "GHSA-c3h9-896r-86jm",
+          "GO-2021-0053"
+        ]
+      }
+    ],
+    "package": {
+      "ecosystem": "Go",
+      "name": "github.com/gogo/protobuf",
+      "version": "1.3.1"
+    },
+    "vulnerabilities": [
+      {
+        "affected": [
+          {
+            "database_specific": {
+              "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2022/03/GHSA-c3h9-896r-86jm/GHSA-c3h9-896r-86jm.json"
+            },
+            "package": {
+              "ecosystem": "Go",
+              "name": "github.com/gogo/protobuf",
+              "purl": "pkg:golang/github.com/gogo/protobuf"
+            },
+            "ranges": [
+              {
+                "events": [
+                  {
+                    "introduced": "0"
+                  },
+                  {
+                    "fixed": "1.3.2"
+                  }
+                ],
+                "type": "SEMVER"
+              }
+            ]
+          }
+        ],
+        "aliases": [
+          "CVE-2021-3121"
+        ],
+        "database_specific": {
+          "cwe_ids": [
+            "CWE-129",
+            "CWE-20"
+          ],
+          "github_reviewed": true,
+          "github_reviewed_at": "2022-03-28T20:28:00Z",
+          "nvd_published_at": "2021-01-11T06:15:00Z",
+          "severity": "HIGH"
+        },
+        "details": "An issue was discovered in GoGo Protobuf before 1.3.2. plugin/unmarshal/unmarshal.go lacks certain index validation, aka the \"skippy peanut butter\" issue.",
+        "id": "GHSA-c3h9-896r-86jm",
+        "modified": "2022-03-28T20:28:00Z",
+        "published": "2022-03-28T20:28:00Z",
+        "references": [
+          {
+            "type": "ADVISORY",
+            "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-3121"
+          },
+          {
+            "type": "WEB",
+            "url": "https://github.com/gogo/protobuf/commit/b03c65ea87cdc3521ede29f62fe3ce239267c1bc"
+          },
+          {
+            "type": "WEB",
+            "url": "https://discuss.hashicorp.com/t/hcsec-2021-23-consul-exposed-to-denial-of-service-in-gogo-protobuf-dependency/29025"
+          },
+          {
+            "type": "PACKAGE",
+            "url": "https://github.com/gogo/protobuf"
+          },
+          {
+            "type": "WEB",
+            "url": "https://github.com/gogo/protobuf/compare/v1.3.1...v1.3.2"
+          },
+          {
+            "type": "WEB",
+            "url": "https://lists.apache.org/thread.html/r68032132c0399c29d6cdc7bd44918535da54060a10a12b1591328bff@%3Cnotifications.skywalking.apache.org%3E"
+          },
+          {
+            "type": "WEB",
+            "url": "https://lists.apache.org/thread.html/r88d69555cb74a129a7bf84838073b61259b4a3830190e05a3b87994e@%3Ccommits.pulsar.apache.org%3E"
+          },
+          {
+            "type": "WEB",
+            "url": "https://lists.apache.org/thread.html/rc1e9ff22c5641d73701ba56362fb867d40ed287cca000b131dcf4a44@%3Ccommits.pulsar.apache.org%3E"
+          },
+          {
+            "type": "WEB",
+            "url": "https://pkg.go.dev/vuln/GO-2021-0053"
+          },
+          {
+            "type": "WEB",
+            "url": "https://security.netapp.com/advisory/ntap-20210219-0006/"
+          }
+        ],
+        "schema_version": "1.3.0",
+        "summary": "Improper Input Validation in GoGo Protobuf"
+      },
+      {
+        "affected": [
+          {
+            "database_specific": {
+              "source": "https://vuln.go.dev/ID/GO-2021-0053.json",
+              "url": "https://pkg.go.dev/vuln/GO-2021-0053"
+            },
+            "ecosystem_specific": {
+              "imports": [
+                {
+                  "path": "github.com/gogo/protobuf/plugin/unmarshal",
+                  "symbols": [
+                    "unmarshal.Generate",
+                    "unmarshal.field"
+                  ]
+                }
+              ]
+            },
+            "package": {
+              "ecosystem": "Go",
+              "name": "github.com/gogo/protobuf",
+              "purl": "pkg:golang/github.com/gogo/protobuf"
+            },
+            "ranges": [
+              {
+                "events": [
+                  {
+                    "introduced": "0"
+                  },
+                  {
+                    "fixed": "1.3.2"
+                  }
+                ],
+                "type": "SEMVER"
+              }
+            ]
+          }
+        ],
+        "aliases": [
+          "CVE-2021-3121",
+          "GHSA-c3h9-896r-86jm"
+        ],
+        "details": "Due to improper bounds checking, maliciously crafted input to generated Unmarshal methods can cause an out-of-bounds panic. If parsing messages from untrusted parties, this may be used as a denial of service vector.",
+        "id": "GO-2021-0053",
+        "modified": "2023-02-10T16:51:38Z",
+        "published": "2021-04-14T20:04:52Z",
+        "references": [
+          {
+            "type": "FIX",
+            "url": "https://github.com/gogo/protobuf/commit/b03c65ea87cdc3521ede29f62fe3ce239267c1bc"
+          }
+        ],
+        "schema_version": "1.3.0",
+        "summary": ""
+      }
+    ]
+  },
+  {
+    "groups": [
+      {
+        "experimentalAnalysis": {
+          "GO-2023-1558": {
+            "called": true
+          }
+        },
+        "ids": [
+          "GHSA-2h6c-j3gf-xp9r",
+          "GO-2023-1558"
+        ]
+      }
+    ],
+    "package": {
+      "ecosystem": "Go",
+      "name": "github.com/ipfs/go-bitfield",
+      "version": "1.0.0"
+    },
+    "vulnerabilities": [
+      {
+        "affected": [
+          {
+            "database_specific": {
+              "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2023/02/GHSA-2h6c-j3gf-xp9r/GHSA-2h6c-j3gf-xp9r.json"
+            },
+            "package": {
+              "ecosystem": "Go",
+              "name": "github.com/ipfs/go-bitfield",
+              "purl": "pkg:golang/github.com/ipfs/go-bitfield"
+            },
+            "ranges": [
+              {
+                "events": [
+                  {
+                    "introduced": "1.0.0"
+                  },
+                  {
+                    "fixed": "1.1.0"
+                  }
+                ],
+                "type": "SEMVER"
+              }
+            ],
+            "versions": [
+              "1.0.0"
+            ]
+          }
+        ],
+        "aliases": [
+          "CVE-2023-23626"
+        ],
+        "database_specific": {
+          "cwe_ids": [
+            "CWE-1284",
+            "CWE-754"
+          ],
+          "github_reviewed": true,
+          "github_reviewed_at": "2023-02-10T19:52:45Z",
+          "nvd_published_at": "2023-02-09T21:15:00Z",
+          "severity": "MODERATE"
+        },
+        "details": "### Impact\nWhen feeding untrusted user input into the size parameter of `NewBitfield` and `FromBytes` functions, an attacker can trigger `panic`s.\n\nThis happen when the `size` is a not a multiple of `8` or is negative.\nThere were already a note in the `NewBitfield` documentation:\n\u003e ```\n\u003e Panics if size is not a multiple of 8.\n\u003e ````\n\nBut it incomplete and missing from `FromBytes`'s documentation.\n\nThis has been replaced by returning an `(Bitfield, error)` and returning a non nil error if the size is wrong.\n\n### Patches\n- https://github.com/ipfs/go-bitfield/commit/5e1d256fe043fc4163343ccca83862c69c52e579\n\n### Workarounds\n- Ensure `size%8 == 0 \u0026\u0026 size \u003e= 0` yourself before calling `NewBitfield` or `FromBytes`\n\n### References\n- https://github.com/ipfs/go-unixfs/security/advisories/GHSA-q264-w97q-q778\n",
+        "id": "GHSA-2h6c-j3gf-xp9r",
+        "modified": "2023-02-10T19:52:45Z",
+        "published": "2023-02-10T19:52:45Z",
+        "references": [
+          {
+            "type": "WEB",
+            "url": "https://github.com/ipfs/go-bitfield/security/advisories/GHSA-2h6c-j3gf-xp9r"
+          },
+          {
+            "type": "ADVISORY",
+            "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-23626"
+          },
+          {
+            "type": "WEB",
+            "url": "https://github.com/ipfs/go-bitfield/commit/5e1d256fe043fc4163343ccca83862c69c52e579"
+          },
+          {
+            "type": "PACKAGE",
+            "url": "https://github.com/ipfs/go-bitfield"
+          },
+          {
+            "type": "WEB",
+            "url": "https://pkg.go.dev/vuln/GO-2023-1558"
+          }
+        ],
+        "schema_version": "1.3.0",
+        "summary": "IPFS go-bitfield vulnerable to DoS via malformed size arguments"
+      },
+      {
+        "affected": [
+          {
+            "database_specific": {
+              "source": "https://vuln.go.dev/ID/GO-2023-1558.json",
+              "url": "https://pkg.go.dev/vuln/GO-2023-1558"
+            },
+            "ecosystem_specific": {
+              "imports": [
+                {
+                  "path": "github.com/ipfs/go-bitfield",
+                  "symbols": [
+                    "FromBytes",
+                    "NewBitfield"
+                  ]
+                }
+              ]
+            },
+            "package": {
+              "ecosystem": "Go",
+              "name": "github.com/ipfs/go-bitfield",
+              "purl": "pkg:golang/github.com/ipfs/go-bitfield"
+            },
+            "ranges": [
+              {
+                "events": [
+                  {
+                    "introduced": "0"
+                  },
+                  {
+                    "fixed": "1.1.0"
+                  }
+                ],
+                "type": "SEMVER"
+              }
+            ]
+          }
+        ],
+        "aliases": [
+          "CVE-2023-23626",
+          "GHSA-2h6c-j3gf-xp9r"
+        ],
+        "details": "When feeding untrusted user input into the size parameter of `NewBitfield` and FromBytes functions, an attacker can trigger panics.\n\nThis happens when the size is a not a multiple of 8 or is negative.\n\nA workaround is to ensure size%8 == 0 \u0026\u0026 size \u003e= 0 yourself before calling NewBitfield or FromBytes.",
+        "id": "GO-2023-1558",
+        "modified": "2023-02-14T19:41:21Z",
+        "published": "2023-02-14T19:41:21Z",
+        "references": [
+          {
+            "type": "ADVISORY",
+            "url": "https://github.com/ipfs/go-bitfield/security/advisories/GHSA-2h6c-j3gf-xp9r"
+          },
+          {
+            "type": "FIX",
+            "url": "https://github.com/ipfs/go-bitfield/commit/5e1d256fe043fc4163343ccca83862c69c52e579"
+          }
+        ],
+        "schema_version": "1.3.0",
+        "summary": ""
+      }
+    ]
+  },
+  {
+    "groups": [
+      {
+        "experimentalAnalysis": {
+          "GO-2023-1572": {
+            "called": false
+          }
+        },
+        "ids": [
+          "GHSA-qgc7-mgm3-q253",
+          "GO-2023-1572"
+        ]
+      }
+    ],
+    "package": {
+      "ecosystem": "Go",
+      "name": "golang.org/x/image",
+      "version": "0.4.0"
+    },
+    "vulnerabilities": [
+      {
+        "affected": [
+          {
+            "database_specific": {
+              "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2023/02/GHSA-qgc7-mgm3-q253/GHSA-qgc7-mgm3-q253.json"
+            },
+            "package": {
+              "ecosystem": "Go",
+              "name": "golang.org/x/image",
+              "purl": "pkg:golang/golang.org/x/image"
+            },
+            "ranges": [
+              {
+                "events": [
+                  {
+                    "introduced": "0"
+                  },
+                  {
+                    "fixed": "0.5.0"
+                  }
+                ],
+                "type": "SEMVER"
+              }
+            ]
+          }
+        ],
+        "aliases": [
+          "CVE-2022-41727"
+        ],
+        "database_specific": {
+          "cwe_ids": [
+            "CWE-400"
+          ],
+          "github_reviewed": true,
+          "github_reviewed_at": "2023-02-17T13:59:44Z",
+          "nvd_published_at": null,
+          "severity": "LOW"
+        },
+        "details": "An attacker can craft a malformed TIFF image which will consume a significant amount of memory when passed to DecodeConfig. This could lead to a denial of service.",
+        "id": "GHSA-qgc7-mgm3-q253",
+        "modified": "2023-02-17T13:59:44Z",
+        "published": "2023-02-17T13:59:44Z",
+        "references": [
+          {
+            "type": "ADVISORY",
+            "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-41727"
+          },
+          {
+            "type": "WEB",
+            "url": "https://go.dev/cl/468195"
+          },
+          {
+            "type": "WEB",
+            "url": "https://go.dev/issue/58003"
+          },
+          {
+            "type": "WEB",
+            "url": "https://groups.google.com/g/golang-announce/c/ag-FiyjlD5o"
+          },
+          {
+            "type": "WEB",
+            "url": "https://pkg.go.dev/vuln/GO-2023-1572"
+          }
+        ],
+        "schema_version": "1.3.0",
+        "summary": "Uncontrolled Resource Consumption"
+      },
+      {
+        "affected": [
+          {
+            "database_specific": {
+              "source": "https://vuln.go.dev/ID/GO-2023-1572.json",
+              "url": "https://pkg.go.dev/vuln/GO-2023-1572"
+            },
+            "ecosystem_specific": {
+              "imports": [
+                {
+                  "path": "golang.org/x/image/tiff",
+                  "symbols": [
+                    "Decode",
+                    "DecodeConfig",
+                    "decoder.ifdUint",
+                    "newDecoder"
+                  ]
+                }
+              ]
+            },
+            "package": {
+              "ecosystem": "Go",
+              "name": "golang.org/x/image",
+              "purl": "pkg:golang/golang.org/x/image"
+            },
+            "ranges": [
+              {
+                "events": [
+                  {
+                    "introduced": "0"
+                  },
+                  {
+                    "fixed": "0.5.0"
+                  }
+                ],
+                "type": "SEMVER"
+              }
+            ]
+          }
+        ],
+        "aliases": [
+          "CVE-2022-41727"
+        ],
+        "details": "An attacker can craft a malformed TIFF image which will consume a significant amount of memory when passed to DecodeConfig. This could lead to a denial of service.",
+        "id": "GO-2023-1572",
+        "modified": "2023-02-16T22:25:24Z",
+        "published": "2023-02-16T22:25:24Z",
+        "references": [
+          {
+            "type": "REPORT",
+            "url": "https://go.dev/issue/58003"
+          },
+          {
+            "type": "FIX",
+            "url": "https://go.dev/cl/468195"
+          },
+          {
+            "type": "WEB",
+            "url": "https://groups.google.com/g/golang-announce/c/ag-FiyjlD5o"
+          }
+        ],
+        "schema_version": "1.3.0",
+        "summary": ""
+      }
+    ]
+  }
+]
\ No newline at end of file
diff --git a/internal/sourceanalysis/fixtures/vulnbyid.json b/internal/sourceanalysis/fixtures/vulnbyid.json
new file mode 100644
index 0000000..5f83174
--- /dev/null
+++ b/internal/sourceanalysis/fixtures/vulnbyid.json
@@ -0,0 +1,407 @@
+{
+    "GHSA-2h6c-j3gf-xp9r": {
+        "schema_version": "1.3.0",
+        "id": "GHSA-2h6c-j3gf-xp9r",
+        "modified": "2023-02-10T19:52:45Z",
+        "published": "2023-02-10T19:52:45Z",
+        "aliases": [
+            "CVE-2023-23626"
+        ],
+        "summary": "IPFS go-bitfield vulnerable to DoS via malformed size arguments",
+        "details": "### Impact\nWhen feeding untrusted user input into the size parameter of `NewBitfield` and `FromBytes` functions, an attacker can trigger `panic`s.\n\nThis happen when the `size` is a not a multiple of `8` or is negative.\nThere were already a note in the `NewBitfield` documentation:\n\u003e ```\n\u003e Panics if size is not a multiple of 8.\n\u003e ````\n\nBut it incomplete and missing from `FromBytes`'s documentation.\n\nThis has been replaced by returning an `(Bitfield, error)` and returning a non nil error if the size is wrong.\n\n### Patches\n- https://github.com/ipfs/go-bitfield/commit/5e1d256fe043fc4163343ccca83862c69c52e579\n\n### Workarounds\n- Ensure `size%8 == 0 \u0026\u0026 size \u003e= 0` yourself before calling `NewBitfield` or `FromBytes`\n\n### References\n- https://github.com/ipfs/go-unixfs/security/advisories/GHSA-q264-w97q-q778\n",
+        "affected": [
+            {
+                "package": {
+                    "ecosystem": "Go",
+                    "name": "github.com/ipfs/go-bitfield",
+                    "purl": "pkg:golang/github.com/ipfs/go-bitfield"
+                },
+                "ranges": [
+                    {
+                        "type": "SEMVER",
+                        "events": [
+                            {
+                                "introduced": "1.0.0"
+                            },
+                            {
+                                "fixed": "1.1.0"
+                            }
+                        ]
+                    }
+                ],
+                "versions": [
+                    "1.0.0"
+                ],
+                "database_specific": {
+                    "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2023/02/GHSA-2h6c-j3gf-xp9r/GHSA-2h6c-j3gf-xp9r.json"
+                }
+            }
+        ],
+        "references": [
+            {
+                "type": "WEB",
+                "url": "https://github.com/ipfs/go-bitfield/security/advisories/GHSA-2h6c-j3gf-xp9r"
+            },
+            {
+                "type": "ADVISORY",
+                "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-23626"
+            },
+            {
+                "type": "WEB",
+                "url": "https://github.com/ipfs/go-bitfield/commit/5e1d256fe043fc4163343ccca83862c69c52e579"
+            },
+            {
+                "type": "PACKAGE",
+                "url": "https://github.com/ipfs/go-bitfield"
+            },
+            {
+                "type": "WEB",
+                "url": "https://pkg.go.dev/vuln/GO-2023-1558"
+            }
+        ],
+        "database_specific": {
+            "cwe_ids": [
+                "CWE-1284",
+                "CWE-754"
+            ],
+            "github_reviewed": true,
+            "github_reviewed_at": "2023-02-10T19:52:45Z",
+            "nvd_published_at": "2023-02-09T21:15:00Z",
+            "severity": "MODERATE"
+        }
+    },
+    "GHSA-c3h9-896r-86jm": {
+        "schema_version": "1.3.0",
+        "id": "GHSA-c3h9-896r-86jm",
+        "modified": "2022-03-28T20:28:00Z",
+        "published": "2022-03-28T20:28:00Z",
+        "aliases": [
+            "CVE-2021-3121"
+        ],
+        "summary": "Improper Input Validation in GoGo Protobuf",
+        "details": "An issue was discovered in GoGo Protobuf before 1.3.2. plugin/unmarshal/unmarshal.go lacks certain index validation, aka the \"skippy peanut butter\" issue.",
+        "affected": [
+            {
+                "package": {
+                    "ecosystem": "Go",
+                    "name": "github.com/gogo/protobuf",
+                    "purl": "pkg:golang/github.com/gogo/protobuf"
+                },
+                "ranges": [
+                    {
+                        "type": "SEMVER",
+                        "events": [
+                            {
+                                "introduced": "0"
+                            },
+                            {
+                                "fixed": "1.3.2"
+                            }
+                        ]
+                    }
+                ],
+                "database_specific": {
+                    "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2022/03/GHSA-c3h9-896r-86jm/GHSA-c3h9-896r-86jm.json"
+                }
+            }
+        ],
+        "references": [
+            {
+                "type": "ADVISORY",
+                "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-3121"
+            },
+            {
+                "type": "WEB",
+                "url": "https://github.com/gogo/protobuf/commit/b03c65ea87cdc3521ede29f62fe3ce239267c1bc"
+            },
+            {
+                "type": "WEB",
+                "url": "https://discuss.hashicorp.com/t/hcsec-2021-23-consul-exposed-to-denial-of-service-in-gogo-protobuf-dependency/29025"
+            },
+            {
+                "type": "PACKAGE",
+                "url": "https://github.com/gogo/protobuf"
+            },
+            {
+                "type": "WEB",
+                "url": "https://github.com/gogo/protobuf/compare/v1.3.1...v1.3.2"
+            },
+            {
+                "type": "WEB",
+                "url": "https://lists.apache.org/thread.html/r68032132c0399c29d6cdc7bd44918535da54060a10a12b1591328bff@%3Cnotifications.skywalking.apache.org%3E"
+            },
+            {
+                "type": "WEB",
+                "url": "https://lists.apache.org/thread.html/r88d69555cb74a129a7bf84838073b61259b4a3830190e05a3b87994e@%3Ccommits.pulsar.apache.org%3E"
+            },
+            {
+                "type": "WEB",
+                "url": "https://lists.apache.org/thread.html/rc1e9ff22c5641d73701ba56362fb867d40ed287cca000b131dcf4a44@%3Ccommits.pulsar.apache.org%3E"
+            },
+            {
+                "type": "WEB",
+                "url": "https://pkg.go.dev/vuln/GO-2021-0053"
+            },
+            {
+                "type": "WEB",
+                "url": "https://security.netapp.com/advisory/ntap-20210219-0006/"
+            }
+        ],
+        "database_specific": {
+            "cwe_ids": [
+                "CWE-129",
+                "CWE-20"
+            ],
+            "github_reviewed": true,
+            "github_reviewed_at": "2022-03-28T20:28:00Z",
+            "nvd_published_at": "2021-01-11T06:15:00Z",
+            "severity": "HIGH"
+        }
+    },
+    "GHSA-qgc7-mgm3-q253": {
+        "schema_version": "1.3.0",
+        "id": "GHSA-qgc7-mgm3-q253",
+        "modified": "2023-02-17T13:59:44Z",
+        "published": "2023-02-17T13:59:44Z",
+        "aliases": [
+            "CVE-2022-41727"
+        ],
+        "summary": "Uncontrolled Resource Consumption",
+        "details": "An attacker can craft a malformed TIFF image which will consume a significant amount of memory when passed to DecodeConfig. This could lead to a denial of service.",
+        "affected": [
+            {
+                "package": {
+                    "ecosystem": "Go",
+                    "name": "golang.org/x/image",
+                    "purl": "pkg:golang/golang.org/x/image"
+                },
+                "ranges": [
+                    {
+                        "type": "SEMVER",
+                        "events": [
+                            {
+                                "introduced": "0"
+                            },
+                            {
+                                "fixed": "0.5.0"
+                            }
+                        ]
+                    }
+                ],
+                "database_specific": {
+                    "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2023/02/GHSA-qgc7-mgm3-q253/GHSA-qgc7-mgm3-q253.json"
+                }
+            }
+        ],
+        "references": [
+            {
+                "type": "ADVISORY",
+                "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-41727"
+            },
+            {
+                "type": "WEB",
+                "url": "https://go.dev/cl/468195"
+            },
+            {
+                "type": "WEB",
+                "url": "https://go.dev/issue/58003"
+            },
+            {
+                "type": "WEB",
+                "url": "https://groups.google.com/g/golang-announce/c/ag-FiyjlD5o"
+            },
+            {
+                "type": "WEB",
+                "url": "https://pkg.go.dev/vuln/GO-2023-1572"
+            }
+        ],
+        "database_specific": {
+            "cwe_ids": [
+                "CWE-400"
+            ],
+            "github_reviewed": true,
+            "github_reviewed_at": "2023-02-17T13:59:44Z",
+            "nvd_published_at": null,
+            "severity": "LOW"
+        }
+    },
+    "GO-2021-0053": {
+        "schema_version": "1.3.0",
+        "id": "GO-2021-0053",
+        "modified": "2023-02-10T16:51:38Z",
+        "published": "2021-04-14T20:04:52Z",
+        "aliases": [
+            "CVE-2021-3121",
+            "GHSA-c3h9-896r-86jm"
+        ],
+        "summary": "",
+        "details": "Due to improper bounds checking, maliciously crafted input to generated Unmarshal methods can cause an out-of-bounds panic. If parsing messages from untrusted parties, this may be used as a denial of service vector.",
+        "affected": [
+            {
+                "package": {
+                    "ecosystem": "Go",
+                    "name": "github.com/gogo/protobuf",
+                    "purl": "pkg:golang/github.com/gogo/protobuf"
+                },
+                "ranges": [
+                    {
+                        "type": "SEMVER",
+                        "events": [
+                            {
+                                "introduced": "0"
+                            },
+                            {
+                                "fixed": "1.3.2"
+                            }
+                        ]
+                    }
+                ],
+                "database_specific": {
+                    "source": "https://vuln.go.dev/ID/GO-2021-0053.json",
+                    "url": "https://pkg.go.dev/vuln/GO-2021-0053"
+                },
+                "ecosystem_specific": {
+                    "imports": [
+                        {
+                            "path": "github.com/gogo/protobuf/plugin/unmarshal",
+                            "symbols": [
+                                "unmarshal.Generate",
+                                "unmarshal.field"
+                            ]
+                        }
+                    ]
+                }
+            }
+        ],
+        "references": [
+            {
+                "type": "FIX",
+                "url": "https://github.com/gogo/protobuf/commit/b03c65ea87cdc3521ede29f62fe3ce239267c1bc"
+            }
+        ]
+    },
+    "GO-2023-1558": {
+        "schema_version": "1.3.0",
+        "id": "GO-2023-1558",
+        "modified": "2023-02-14T19:41:21Z",
+        "published": "2023-02-14T19:41:21Z",
+        "aliases": [
+            "CVE-2023-23626",
+            "GHSA-2h6c-j3gf-xp9r"
+        ],
+        "summary": "",
+        "details": "When feeding untrusted user input into the size parameter of `NewBitfield` and FromBytes functions, an attacker can trigger panics.\n\nThis happens when the size is a not a multiple of 8 or is negative.\n\nA workaround is to ensure size%8 == 0 \u0026\u0026 size \u003e= 0 yourself before calling NewBitfield or FromBytes.",
+        "affected": [
+            {
+                "package": {
+                    "ecosystem": "Go",
+                    "name": "github.com/ipfs/go-bitfield",
+                    "purl": "pkg:golang/github.com/ipfs/go-bitfield"
+                },
+                "ranges": [
+                    {
+                        "type": "SEMVER",
+                        "events": [
+                            {
+                                "introduced": "0"
+                            },
+                            {
+                                "fixed": "1.1.0"
+                            }
+                        ]
+                    }
+                ],
+                "database_specific": {
+                    "source": "https://vuln.go.dev/ID/GO-2023-1558.json",
+                    "url": "https://pkg.go.dev/vuln/GO-2023-1558"
+                },
+                "ecosystem_specific": {
+                    "imports": [
+                        {
+                            "path": "github.com/ipfs/go-bitfield",
+                            "symbols": [
+                                "FromBytes",
+                                "NewBitfield"
+                            ]
+                        }
+                    ]
+                }
+            }
+        ],
+        "references": [
+            {
+                "type": "ADVISORY",
+                "url": "https://github.com/ipfs/go-bitfield/security/advisories/GHSA-2h6c-j3gf-xp9r"
+            },
+            {
+                "type": "FIX",
+                "url": "https://github.com/ipfs/go-bitfield/commit/5e1d256fe043fc4163343ccca83862c69c52e579"
+            }
+        ]
+    },
+    "GO-2023-1572": {
+        "schema_version": "1.3.0",
+        "id": "GO-2023-1572",
+        "modified": "2023-02-22T20:13:12Z",
+        "published": "2023-02-16T22:25:24Z",
+        "aliases": [
+            "CVE-2022-41727",
+            "GHSA-qgc7-mgm3-q253"
+        ],
+        "summary": "",
+        "details": "An attacker can craft a malformed TIFF image which will consume a significant amount of memory when passed to DecodeConfig. This could lead to a denial of service.",
+        "affected": [
+            {
+                "package": {
+                    "ecosystem": "Go",
+                    "name": "golang.org/x/image",
+                    "purl": "pkg:golang/golang.org/x/image"
+                },
+                "ranges": [
+                    {
+                        "type": "SEMVER",
+                        "events": [
+                            {
+                                "introduced": "0"
+                            },
+                            {
+                                "fixed": "0.5.0"
+                            }
+                        ]
+                    }
+                ],
+                "database_specific": {
+                    "source": "https://vuln.go.dev/ID/GO-2023-1572.json",
+                    "url": "https://pkg.go.dev/vuln/GO-2023-1572"
+                },
+                "ecosystem_specific": {
+                    "imports": [
+                        {
+                            "path": "golang.org/x/image/tiff",
+                            "symbols": [
+                                "Decode",
+                                "DecodeConfig",
+                                "decoder.ifdUint",
+                                "newDecoder"
+                            ]
+                        }
+                    ]
+                }
+            }
+        ],
+        "references": [
+            {
+                "type": "REPORT",
+                "url": "https://go.dev/issue/58003"
+            },
+            {
+                "type": "FIX",
+                "url": "https://go.dev/cl/468195"
+            },
+            {
+                "type": "WEB",
+                "url": "https://groups.google.com/g/golang-announce/c/ag-FiyjlD5o"
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/internal/sourceanalysis/go.go b/internal/sourceanalysis/go.go
new file mode 100644
index 0000000..a2a599a
--- /dev/null
+++ b/internal/sourceanalysis/go.go
@@ -0,0 +1,85 @@
+package sourceanalysis
+
+import (
+	"fmt"
+	"path/filepath"
+
+	"github.com/google/osv-scanner/internal/govulncheckshim"
+	"github.com/google/osv-scanner/internal/output"
+	"github.com/google/osv-scanner/pkg/models"
+	"golang.org/x/exp/slices"
+	"golang.org/x/vuln/exp/govulncheck"
+)
+
+func goAnalysis(r *output.Reporter, pkgs []models.PackageVulns, source models.SourceInfo) {
+	vulns, vulnsByID := vulnsFromAllPkgs(pkgs)
+	res, err := govulncheckshim.RunGoVulnCheck(filepath.Dir(source.Path), vulns)
+	if err != nil {
+		// TODO: Better method to identify the type of error and give advice specific to the error
+		r.PrintError(
+			fmt.Sprintf("Failed to run code analysis (govulncheck) on '%s' because %s\n"+
+				"(the Go toolchain is required)\n", source.Path, err.Error()))
+
+		return
+	}
+	gvcResByVulnID := map[string]*govulncheck.Vuln{}
+	for _, v := range res.Vulns {
+		gvcResByVulnID[v.OSV.ID] = v
+	}
+	matchAnalysisWithPackageVulns(pkgs, gvcResByVulnID, vulnsByID)
+}
+
+func matchAnalysisWithPackageVulns(pkgs []models.PackageVulns, gvcResByVulnID map[string]*govulncheck.Vuln, vulnsByID map[string]models.Vulnerability) {
+	for _, pv := range pkgs {
+		// Use index to keep reference to original element in slice
+		for groupIdx := range pv.Groups {
+			for _, vulnID := range pv.Groups[groupIdx].IDs {
+				analysis := &pv.Groups[groupIdx].ExperimentalAnalysis
+				if *analysis == nil {
+					*analysis = make(map[string]models.AnalysisInfo)
+				}
+
+				gvcVuln, ok := gvcResByVulnID[vulnID]
+				if !ok { // If vulnerability not found, check if it contain any source information
+					fillNotImportedAnalysisInfo(vulnsByID, vulnID, pv, analysis)
+					continue
+				}
+				// Module list is unlikely to be very big, linear search is fine
+				containsModule := slices.ContainsFunc(gvcVuln.Modules, func(module *govulncheck.Module) bool {
+					return module.Path == pv.Package.Name
+				})
+
+				if !containsModule {
+					// Code does not import module, so definitely not called
+					(*analysis)[vulnID] = models.AnalysisInfo{
+						Called: false,
+					}
+				} else {
+					// Code does import module, check if it's called
+					(*analysis)[vulnID] = models.AnalysisInfo{
+						Called: gvcVuln.IsCalled(),
+					}
+				}
+			}
+		}
+	}
+}
+
+// fillNotImportedAnalysisInfo checks for any source information in advisories, and sets called to false
+func fillNotImportedAnalysisInfo(vulnsByID map[string]models.Vulnerability, vulnID string, pv models.PackageVulns, analysis *map[string]models.AnalysisInfo) {
+	for _, v := range vulnsByID[vulnID].Affected {
+		// TODO: Compare versions to see if this is the correct affected element
+		// ver, err := semantic.Parse(pv.Package.Version, semantic.SemverVersion)
+		if v.Package.Name != pv.Package.Name {
+			continue
+		}
+		_, hasImportsField := v.EcosystemSpecific["imports"]
+		if hasImportsField {
+			// If there is source information, then analysis has been performed, and
+			// code does not import the vulnerable package, so definitely not called
+			(*analysis)[vulnID] = models.AnalysisInfo{
+				Called: false,
+			}
+		}
+	}
+}
diff --git a/internal/sourceanalysis/sourceanalysis.go b/internal/sourceanalysis/sourceanalysis.go
new file mode 100644
index 0000000..121233b
--- /dev/null
+++ b/internal/sourceanalysis/sourceanalysis.go
@@ -0,0 +1,33 @@
+package sourceanalysis
+
+import (
+	"path/filepath"
+
+	"github.com/google/osv-scanner/internal/output"
+	"github.com/google/osv-scanner/pkg/models"
+)
+
+// vulnsFromAllPkgs returns the flattened list of unique vulnerabilities
+func vulnsFromAllPkgs(pkgs []models.PackageVulns) ([]models.Vulnerability, map[string]models.Vulnerability) {
+	flatVulns := map[string]models.Vulnerability{}
+	for _, pv := range pkgs {
+		for _, vuln := range pv.Vulnerabilities {
+			flatVulns[vuln.ID] = vuln
+		}
+	}
+
+	vulns := []models.Vulnerability{}
+	for _, v := range flatVulns {
+		vulns = append(vulns, v)
+	}
+
+	return vulns, flatVulns
+}
+
+// Run runs the language specific analyzers on the code given packages and source info
+func Run(r *output.Reporter, source models.SourceInfo, pkgs []models.PackageVulns) {
+	// GoVulnCheck
+	if source.Type == "lockfile" && filepath.Base(source.Path) == "go.mod" {
+		goAnalysis(r, pkgs, source)
+	}
+}
diff --git a/pkg/models/results.go b/pkg/models/results.go
index 457cf07..76f63b8 100644
--- a/pkg/models/results.go
+++ b/pkg/models/results.go
@@ -1,6 +1,10 @@
 package models
 
-import "time"
+import (
+	"time"
+
+	"golang.org/x/exp/slices"
+)
 
 // Combined vulnerabilities found for the scanned packages
 type VulnerabilityResults struct {
@@ -13,10 +17,13 @@ func (vulns *VulnerabilityResults) Flatten() []VulnerabilityFlattened {
 	for _, res := range vulns.Results {
 		for _, pkg := range res.Packages {
 			for _, v := range pkg.Vulnerabilities {
+				// groupIdx should never be -1 since vulnerabilities should always be in one group
+				groupIdx := slices.IndexFunc(pkg.Groups, func(g GroupInfo) bool { return slices.Contains(g.IDs, v.ID) })
 				results = append(results, VulnerabilityFlattened{
 					Source:        res.Source,
 					Package:       pkg.Package,
 					Vulnerability: v,
+					GroupInfo:     pkg.Groups[groupIdx],
 				})
 			}
 		}
@@ -30,6 +37,7 @@ type VulnerabilityFlattened struct {
 	Source        SourceInfo
 	Package       PackageInfo
 	Vulnerability Vulnerability
+	GroupInfo     GroupInfo
 }
 
 type Vulnerability struct {
@@ -91,6 +99,28 @@ type PackageVulns struct {
 
 type GroupInfo struct {
 	IDs []string `json:"ids"`
+	// Map of Vulnerability IDs to AnalysisInfo
+	ExperimentalAnalysis map[string]AnalysisInfo `json:"experimentalAnalysis,omitempty"`
+}
+
+// IsCalled returns true if any analysis performed determines that the vulnerability is being called
+// Also returns true if no analysis is performed
+func (groupInfo *GroupInfo) IsCalled() bool {
+	if len(groupInfo.ExperimentalAnalysis) == 0 {
+		return true
+	}
+
+	for _, analysis := range groupInfo.ExperimentalAnalysis {
+		if analysis.Called {
+			return true
+		}
+	}
+
+	return false
+}
+
+type AnalysisInfo struct {
+	Called bool `json:"called"`
 }
 
 // Specific package information
diff --git a/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go
index f7f33b1..6b1d8e5 100644
--- a/pkg/osvscanner/osvscanner.go
+++ b/pkg/osvscanner/osvscanner.go
@@ -32,6 +32,8 @@ type ScannerActions struct {
 	NoIgnore             bool
 	DockerContainerNames []string
 	ConfigOverridePath   string
+
+	ExperimentalCallAnalysis bool
 }
 
 // NoPackagesFoundErr for when no packages is found during a scan.
@@ -42,6 +44,9 @@ var NoPackagesFoundErr = errors.New("no packages found in scan")
 //nolint:errname,stylecheck // Would require version major bump to change
 var VulnerabilitiesFoundErr = errors.New("vulnerabilities found")
 
+//nolint:errname,stylecheck // Would require version bump to change
+var OnlyUncalledVulnerabilitiesFoundErr = errors.New("only uncalled vulnerabilities found")
+
 // scanDir walks through the given directory to try to find any relevant files
 // These include:
 //   - Any lockfiles with scanLockfile
@@ -455,16 +460,22 @@ func DoScan(actions ScannerActions, r *output.Reporter) (models.VulnerabilityRes
 	if filtered > 0 {
 		r.PrintText(fmt.Sprintf("Filtered %d vulnerabilities from output\n", filtered))
 	}
-
 	hydratedResp, err := osv.Hydrate(resp)
 	if err != nil {
 		return models.VulnerabilityResults{}, fmt.Errorf("failed to hydrate OSV response: %w", err)
 	}
 
-	vulnerabilityResults := groupResponseBySource(r, query, hydratedResp)
+	vulnerabilityResults := groupResponseBySource(r, query, hydratedResp, actions.ExperimentalCallAnalysis)
 	// if vulnerability exists it should return error
 	if len(vulnerabilityResults.Results) > 0 {
-		return vulnerabilityResults, VulnerabilitiesFoundErr
+		// If any vulnerabilities are called, then we return VulnerabilitiesFoundErr
+		for _, vf := range vulnerabilityResults.Flatten() {
+			if vf.GroupInfo.IsCalled() {
+				return vulnerabilityResults, VulnerabilitiesFoundErr
+			}
+		}
+		// Otherwise return OnlyUncalledVulnerabilitiesFoundErr
+		return vulnerabilityResults, OnlyUncalledVulnerabilitiesFoundErr
 	}
 
 	return vulnerabilityResults, nil
diff --git a/pkg/osvscanner/vulnerability_result.go b/pkg/osvscanner/vulnerability_result.go
index 5196aae..fab5381 100644
--- a/pkg/osvscanner/vulnerability_result.go
+++ b/pkg/osvscanner/vulnerability_result.go
@@ -5,6 +5,7 @@ import (
 	"sort"
 
 	"github.com/google/osv-scanner/internal/output"
+	"github.com/google/osv-scanner/internal/sourceanalysis"
 	"github.com/google/osv-scanner/pkg/grouper"
 	"github.com/google/osv-scanner/pkg/models"
 	"github.com/google/osv-scanner/pkg/osv"
@@ -12,7 +13,7 @@ import (
 
 // groupResponseBySource converts raw OSV API response into structured vulnerability information
 // grouped by source location.
-func groupResponseBySource(r *output.Reporter, query osv.BatchedQuery, resp *osv.HydratedBatchedResponse) models.VulnerabilityResults {
+func groupResponseBySource(r *output.Reporter, query osv.BatchedQuery, resp *osv.HydratedBatchedResponse, callAnalysis bool) models.VulnerabilityResults {
 	output := models.VulnerabilityResults{
 		Results: []models.PackageSource{},
 	}
@@ -48,11 +49,15 @@ func groupResponseBySource(r *output.Reporter, query osv.BatchedQuery, resp *osv
 
 		pkg.Vulnerabilities = response.Vulns
 
-		pkg.Groups = grouper.Group(grouper.ConvertVulnerabilityToIDAliases(pkg.Vulnerabilities))
+		groups := grouper.Group(grouper.ConvertVulnerabilityToIDAliases(pkg.Vulnerabilities))
+		pkg.Groups = groups
 		groupedBySource[query.Source] = append(groupedBySource[query.Source], pkg)
 	}
 
 	for source, packages := range groupedBySource {
+		if callAnalysis {
+			sourceanalysis.Run(r, source, packages)
+		}
 		output.Results = append(output.Results, models.PackageSource{
 			Source:   source,
 			Packages: packages,


Test patch:
diff --git a/internal/govulncheckshim/client_test.go b/internal/govulncheckshim/client_test.go
new file mode 100644
index 0000000..778973b
--- /dev/null
+++ b/internal/govulncheckshim/client_test.go
@@ -0,0 +1,244 @@
+package govulncheckshim
+
+import (
+	"context"
+	"encoding/json"
+	"os"
+	"path/filepath"
+	"testing"
+
+	"github.com/google/osv-scanner/pkg/models"
+)
+
+func newTestClient(t *testing.T) *localSource {
+	t.Helper()
+
+	vulns := []models.Vulnerability{}
+	entries, err := os.ReadDir("fixtures/client")
+	if err != nil {
+		t.Fatal(err)
+	}
+	for _, entry := range entries {
+		file, err := os.Open(filepath.Join("fixtures/client", entry.Name()))
+		if err != nil {
+			t.Fatalf("failed to open test fixtures %s", err)
+		}
+		newVuln := models.Vulnerability{}
+		err = json.NewDecoder(file).Decode(&newVuln)
+		if err != nil {
+			t.Fatalf("failed to parse test fixtures %s", err)
+		}
+		vulns = append(vulns, newVuln)
+	}
+
+	return newClient(vulns)
+}
+
+func TestGetByID(t *testing.T) {
+	t.Parallel()
+
+	type IDTestCases struct {
+		ID          string
+		ReturnValue *struct {
+			Published string
+		}
+	}
+
+	testCases := []IDTestCases{
+		{
+			ID: "GO-2021-0098",
+			ReturnValue: &struct{ Published string }{
+				Published: "2021-04-14",
+			},
+		},
+		{
+			ID: "GO-2022-0569",
+			ReturnValue: &struct{ Published string }{
+				Published: "2022-08-23",
+			},
+		},
+		{
+			ID: "GHSA-vc3p-29h2-gpcp",
+			ReturnValue: &struct{ Published string }{
+				Published: "2022-01-02",
+			},
+		},
+		{
+			ID:          "GO-1234-5678",
+			ReturnValue: nil,
+		},
+	}
+
+	client := newTestClient(t)
+	for _, x := range testCases {
+		res, err := client.GetByID(context.Background(), x.ID)
+		if err != nil {
+			t.Error(err)
+		}
+		if (x.ReturnValue == nil) != (res == nil) {
+			t.Errorf("Expected %s, found %s", x.ReturnValue, res)
+		}
+
+		if res == nil {
+			return
+		}
+
+		if res.Published.Format("2006-01-02") != x.ReturnValue.Published {
+			t.Errorf("Expected %s, found %s", x.ReturnValue.Published, res.Published.Format("yyyy-mm-dd"))
+		}
+	}
+}
+
+func TestGetByAlias(t *testing.T) {
+	t.Parallel()
+
+	type AliasTestCases struct {
+		Alias       string
+		ReturnValue []struct {
+			ID string
+		}
+	}
+
+	testCases := []AliasTestCases{
+		{
+			Alias: "CVE-2021-44716",
+			ReturnValue: []struct{ ID string }{
+				{
+					ID: "GHSA-vc3p-29h2-gpcp",
+				},
+			},
+		},
+		{
+			Alias: "CVE-2021-21237",
+			ReturnValue: []struct{ ID string }{
+				// Order doesn't matter
+				{
+					ID: "GHSA-cx3w-xqmc-84g5",
+				},
+				{
+					ID: "GO-2021-0098",
+				},
+			},
+		},
+		{
+			Alias: "GHSA-cx3w-xqmc-84g5",
+			ReturnValue: []struct{ ID string }{
+				// If the alias is the ID, do not return the entry
+				// TODO: Is this correct behavior?
+				// {
+				// 	ID: "GHSA-cx3w-xqmc-84g5",
+				// },
+				{
+					ID: "GO-2021-0098",
+				},
+			},
+		},
+		{
+			Alias:       "GO-1234-5678",
+			ReturnValue: nil,
+		},
+	}
+
+	client := newTestClient(t)
+	for _, x := range testCases {
+		res, err := client.GetByAlias(context.Background(), x.Alias)
+		if err != nil {
+			t.Error(err)
+		}
+		if (x.ReturnValue == nil) != (res == nil) {
+			t.Errorf("Expected %v, found %v", x.ReturnValue, res)
+		}
+
+		if res == nil {
+			return
+		}
+
+		if len(res) != len(x.ReturnValue) {
+			t.Errorf("Expected %v, found %v", x.ReturnValue, res)
+		}
+
+		for i := range res {
+			if res[i].ID != x.ReturnValue[i].ID {
+				t.Errorf("Expected %s, found %s", x.ReturnValue[i].ID, res[i].ID)
+			}
+		}
+	}
+}
+
+func TestGetByModule(t *testing.T) {
+	t.Parallel()
+
+	type ModuleTestCases struct {
+		Module      string
+		ReturnValue []struct {
+			ID string
+		}
+	}
+
+	testCases := []ModuleTestCases{
+		{
+			Module: "github.com/beego/beego",
+			ReturnValue: []struct{ ID string }{
+				{
+					ID: "GO-2022-0569",
+				},
+			},
+		},
+		{
+			Module: "github.com/git-lfs/git-lfs",
+			ReturnValue: []struct{ ID string }{
+				// Order doesn't matter
+				{
+					ID: "GHSA-cx3w-xqmc-84g5",
+				},
+				{
+					ID: "GO-2021-0098",
+				},
+			},
+		},
+		{
+			Module:      "golang.org/x/y/z",
+			ReturnValue: nil,
+		},
+	}
+
+	client := newTestClient(t)
+	for _, x := range testCases {
+		res, err := client.GetByModule(context.Background(), x.Module)
+		if err != nil {
+			t.Error(err)
+		}
+		if (x.ReturnValue == nil) != (res == nil) {
+			t.Errorf("Expected %v, found %v", x.ReturnValue, res)
+		}
+
+		if res == nil {
+			return
+		}
+
+		if len(res) != len(x.ReturnValue) {
+			t.Errorf("Expected %v, found %v", x.ReturnValue, res)
+		}
+
+		for i := range res {
+			if res[i].ID != x.ReturnValue[i].ID {
+				t.Errorf("Expected %s, found %s", x.ReturnValue[i].ID, res[i].ID)
+			}
+		}
+	}
+}
+
+func TestLastModifiedTime(t *testing.T) {
+	t.Parallel()
+
+	client := newTestClient(t)
+	clientLastModTime, err := client.LastModifiedTime(context.Background())
+	if err != nil {
+		t.Error(err)
+	}
+
+	// Last modified time in GHSA-vc3p-29h2-gpcp
+	if clientLastModTime.Format("2006-01-02") != "2023-02-08" {
+		t.Errorf("Expected %s, found %s", "2023-02-08", clientLastModTime.Format("2006-01-02"))
+	}
+}
diff --git a/internal/govulncheckshim/fixtures/snapshots/govulncheckshim_test.json b/internal/govulncheckshim/fixtures/snapshots/govulncheckshim_test.json
new file mode 100755
index 0000000..6732f43
--- /dev/null
+++ b/internal/govulncheckshim/fixtures/snapshots/govulncheckshim_test.json
@@ -0,0 +1,172 @@
+{
+ "Vulns": [
+  {
+   "Modules": [
+    {
+     "FixedVersion": "v1.1.0",
+     "FoundVersion": "v1.0.0",
+     "Packages": [
+      {
+       "CallStacks": [
+        {
+         "Frames": [
+          {
+           "FuncName": "main",
+           "PkgPath": "github.com/ossf-tests/osv-e2e",
+           "Position": {
+            "Column": 22,
+            "Filename": "<Any value>",
+            "Line": 13,
+            "Offset": 234
+           },
+           "RecvType": ""
+          },
+          {
+           "FuncName": "NewBitfield",
+           "PkgPath": "github.com/ipfs/go-bitfield",
+           "Position": {
+            "Column": 0,
+            "Filename": "",
+            "Line": 0,
+            "Offset": 0
+           },
+           "RecvType": ""
+          }
+         ],
+         "Summary": "fixtures/test-project/main.go:13:22: github.com/ossf-tests/osv-e2e.main calls github.com/ipfs/go-bitfield.NewBitfield",
+         "Symbol": "NewBitfield"
+        }
+       ],
+       "Path": "github.com/ipfs/go-bitfield"
+      }
+     ],
+     "Path": "github.com/ipfs/go-bitfield"
+    }
+   ],
+   "OSV": {
+    "affected": [
+     {
+      "database_specific": {
+       "url": "https://pkg.go.dev/vuln/GO-2023-1558"
+      },
+      "ecosystem_specific": {
+       "imports": [
+        {
+         "path": "github.com/ipfs/go-bitfield",
+         "symbols": [
+          "FromBytes",
+          "NewBitfield"
+         ]
+        }
+       ]
+      },
+      "package": {
+       "ecosystem": "Go",
+       "name": "github.com/ipfs/go-bitfield"
+      },
+      "ranges": [
+       {
+        "events": [
+         {
+          "introduced": "0"
+         },
+         {
+          "fixed": "1.1.0"
+         }
+        ],
+        "type": "SEMVER"
+       }
+      ]
+     }
+    ],
+    "aliases": [
+     "CVE-2023-23626",
+     "GHSA-2h6c-j3gf-xp9r"
+    ],
+    "details": "When feeding untrusted user input into the size parameter of `NewBitfield` and FromBytes functions, an attacker can trigger panics.\n\nThis happens when the size is a not a multiple of 8 or is negative.\n\nA workaround is to ensure size%8 == 0 \u0026\u0026 size \u003e= 0 yourself before calling NewBitfield or FromBytes.",
+    "id": "GO-2023-1558",
+    "modified": "2023-02-14T19:41:21Z",
+    "published": "2023-02-14T19:41:21Z",
+    "references": [
+     {
+      "type": "ADVISORY",
+      "url": "https://github.com/ipfs/go-bitfield/security/advisories/GHSA-2h6c-j3gf-xp9r"
+     },
+     {
+      "type": "FIX",
+      "url": "https://github.com/ipfs/go-bitfield/commit/5e1d256fe043fc4163343ccca83862c69c52e579"
+     }
+    ],
+    "schema_version": "1.3.1"
+   }
+  },
+  {
+   "Modules": [
+    {
+     "FixedVersion": "v1.3.2",
+     "FoundVersion": "v1.3.1",
+     "Packages": [
+      {
+       "CallStacks": null,
+       "Path": "github.com/gogo/protobuf/plugin/unmarshal"
+      }
+     ],
+     "Path": "github.com/gogo/protobuf"
+    }
+   ],
+   "OSV": {
+    "affected": [
+     {
+      "database_specific": {
+       "url": "https://pkg.go.dev/vuln/GO-2021-0053"
+      },
+      "ecosystem_specific": {
+       "imports": [
+        {
+         "path": "github.com/gogo/protobuf/plugin/unmarshal",
+         "symbols": [
+          "unmarshal.Generate",
+          "unmarshal.field"
+         ]
+        }
+       ]
+      },
+      "package": {
+       "ecosystem": "Go",
+       "name": "github.com/gogo/protobuf"
+      },
+      "ranges": [
+       {
+        "events": [
+         {
+          "introduced": "0"
+         },
+         {
+          "fixed": "1.3.2"
+         }
+        ],
+        "type": "SEMVER"
+       }
+      ]
+     }
+    ],
+    "aliases": [
+     "CVE-2021-3121",
+     "GHSA-c3h9-896r-86jm"
+    ],
+    "details": "Due to improper bounds checking, maliciously crafted input to generated Unmarshal methods can cause an out-of-bounds panic. If parsing messages from untrusted parties, this may be used as a denial of service vector.",
+    "id": "GO-2021-0053",
+    "modified": "2023-02-10T16:51:38Z",
+    "published": "2021-04-14T20:04:52Z",
+    "references": [
+     {
+      "type": "FIX",
+      "url": "https://github.com/gogo/protobuf/commit/b03c65ea87cdc3521ede29f62fe3ce239267c1bc"
+     }
+    ],
+    "schema_version": "1.3.1"
+   }
+  }
+ ]
+}
+
diff --git a/internal/govulncheckshim/fixtures/test-project/go.mod b/internal/govulncheckshim/fixtures/test-project/go.mod
new file mode 100644
index 0000000..999838e
--- /dev/null
+++ b/internal/govulncheckshim/fixtures/test-project/go.mod
@@ -0,0 +1,9 @@
+module github.com/ossf-tests/osv-e2e
+
+go 1.19
+
+require github.com/gogo/protobuf v1.3.1
+
+require github.com/ipfs/go-bitfield v1.0.0
+
+require golang.org/x/image v0.4.0 // indirect
diff --git a/internal/govulncheckshim/fixtures/test-project/go.sum b/internal/govulncheckshim/fixtures/test-project/go.sum
new file mode 100644
index 0000000..d4d9522
--- /dev/null
+++ b/internal/govulncheckshim/fixtures/test-project/go.sum
@@ -0,0 +1,33 @@
+github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
+github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
+github.com/ipfs/go-bitfield v1.0.0 h1:y/XHm2GEmD9wKngheWNNCNL0pzrWXZwCdQGv1ikXknQ=
+github.com/ipfs/go-bitfield v1.0.0/go.mod h1:N/UiujQy+K+ceU1EF5EkVd1TNqevLrCQMIcAEPrdtus=
+github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/image v0.4.0 h1:x1RWAiZIvERqkltrFjtQP1ycmiR5pmhjtCfVOtdURuQ=
+golang.org/x/image v0.4.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/internal/govulncheckshim/fixtures/test-project/main.go b/internal/govulncheckshim/fixtures/test-project/main.go
new file mode 100644
index 0000000..65261ae
--- /dev/null
+++ b/internal/govulncheckshim/fixtures/test-project/main.go
@@ -0,0 +1,14 @@
+package main
+
+import (
+	"github.com/gogo/protobuf/plugin/unmarshal"
+	"github.com/gogo/protobuf/version"
+	"github.com/ipfs/go-bitfield"
+)
+
+func main() {
+	print(version.AtLeast("v1.2.3"))
+	unmarshal.NewUnmarshal()
+
+	bitfield.NewBitfield(14)
+}
diff --git a/internal/govulncheckshim/govulncheckshim_test.go b/internal/govulncheckshim/govulncheckshim_test.go
new file mode 100644
index 0000000..ef54557
--- /dev/null
+++ b/internal/govulncheckshim/govulncheckshim_test.go
@@ -0,0 +1,51 @@
+package govulncheckshim
+
+import (
+	"encoding/json"
+	"os"
+	"path/filepath"
+	"strings"
+	"testing"
+
+	"github.com/google/osv-scanner/internal/testutility"
+	"github.com/google/osv-scanner/pkg/models"
+)
+
+func Test_RunGoVulnCheck(t *testing.T) {
+	t.Parallel()
+	entries, err := os.ReadDir("fixtures")
+	if err != nil {
+		t.Errorf("failed to read fixtures dir: %v", err)
+	}
+
+	vulns := []models.Vulnerability{}
+	for _, de := range entries {
+		if !de.Type().IsRegular() {
+			continue
+		}
+
+		if !strings.HasSuffix(de.Name(), ".json") {
+			continue
+		}
+
+		file, err := os.Open(filepath.Join("fixtures", de.Name()))
+		if err != nil {
+			t.Errorf("failed to open fixture vuln files: %v", err)
+		}
+
+		newVuln := models.Vulnerability{}
+		err = json.NewDecoder(file).Decode(&newVuln)
+		if err != nil {
+			t.Errorf("failed to decode fixture vuln files: %v", err)
+		}
+		vulns = append(vulns, newVuln)
+	}
+
+	res, err := RunGoVulnCheck("fixtures/test-project", vulns)
+	if err != nil {
+		t.Errorf("failed to run RunGoVulnCheck: %v", err)
+	}
+
+	res.Vulns[0].Modules[0].Packages[0].CallStacks[0].Frames[0].Position.Filename = "<Any value>"
+	testutility.AssertMatchFixtureJSON(t, "fixtures/snapshots/govulncheckshim_test.json", res)
+}
diff --git a/internal/sourceanalysis/go_test.go b/internal/sourceanalysis/go_test.go
new file mode 100644
index 0000000..6723f19
--- /dev/null
+++ b/internal/sourceanalysis/go_test.go
@@ -0,0 +1,20 @@
+package sourceanalysis
+
+import (
+	"testing"
+
+	"github.com/google/osv-scanner/internal/testutility"
+	"github.com/google/osv-scanner/pkg/models"
+	"golang.org/x/vuln/exp/govulncheck"
+)
+
+func Test_matchAnalysisWithPackageVulns(t *testing.T) {
+	t.Parallel()
+
+	pkgs := testutility.LoadJSONFixture[[]models.PackageVulns](t, "fixtures/input.json")
+	gvcResByVulnID := testutility.LoadJSONFixture[map[string]*govulncheck.Vuln](t, "fixtures/govulncheckinput.json")
+	vulnsByID := testutility.LoadJSONFixture[map[string]models.Vulnerability](t, "fixtures/vulnbyid.json")
+
+	matchAnalysisWithPackageVulns(pkgs, gvcResByVulnID, vulnsByID)
+	testutility.AssertMatchFixtureJSON(t, "fixtures/output.json", pkgs)
+}
diff --git a/internal/testutility/utility.go b/internal/testutility/utility.go
new file mode 100644
index 0000000..ff64979
--- /dev/null
+++ b/internal/testutility/utility.go
@@ -0,0 +1,63 @@
+package testutility
+
+import (
+	"encoding/json"
+	"os"
+	"reflect"
+	"strings"
+	"testing"
+
+	"github.com/kr/pretty"
+)
+
+// LoadJSONFixture loads a JSON fixture file and returns the decoded version.
+func LoadJSONFixture[V any](t *testing.T, path string) V {
+	t.Helper()
+	file, err := os.Open(path)
+	if err != nil {
+		t.Fatalf("Failed to open fixture: %s", err)
+	}
+	var value V
+	err = json.NewDecoder(file).Decode(&value)
+	if err != nil {
+		t.Fatalf("Failed to parse fixture: %s", err)
+	}
+
+	return value
+}
+
+// AssertMatchFixtureJSON matches the JSON at path with the value val, failing if not equal, printing out the difference.
+func AssertMatchFixtureJSON[V any](t *testing.T, path string, val V) {
+	t.Helper()
+	fileA, err := os.ReadFile(path)
+	if err != nil {
+		t.Fatalf("Failed to open fixture: %s", err)
+	}
+
+	var elem V
+	err = json.Unmarshal(fileA, &elem)
+	if err != nil {
+		t.Fatalf("Failed to unmarshal val: %s", err)
+	}
+
+	if !reflect.DeepEqual(val, elem) {
+		t.Errorf("Not equal: \n%s", strings.Join(pretty.Diff(val, elem), "\n"))
+	}
+}
+
+// CreateJSONFixture creates a JSON file at path of the value val,
+// can be used with AssertMatchFixtureJSON to compare against future values.
+func CreateJSONFixture[V any](t *testing.T, path string, val V) {
+	t.Helper()
+	file, err := os.Open(path)
+	if err != nil {
+		t.Fatalf("Failed to open file to write: %s", err)
+	}
+
+	encoder := json.NewEncoder(file)
+	encoder.SetIndent("", "  ")
+	err = encoder.Encode(val)
+	if err != nil {
+		t.Fatalf("Failed to encode val: %s", err)
+	}
+}
diff --git a/pkg/osvscanner/vulnerability_result_internal_test.go b/pkg/osvscanner/vulnerability_result_internal_test.go
index 97f5621..79e860b 100644
--- a/pkg/osvscanner/vulnerability_result_internal_test.go
+++ b/pkg/osvscanner/vulnerability_result_internal_test.go
@@ -12,9 +12,10 @@ import (
 func Test_groupResponseBySource(t *testing.T) {
 	t.Parallel()
 	type args struct {
-		r     *output.Reporter
-		query osv.BatchedQuery
-		resp  *osv.HydratedBatchedResponse
+		r            *output.Reporter
+		query        osv.BatchedQuery
+		resp         *osv.HydratedBatchedResponse
+		callAnalysis bool
 	}
 	tests := []struct {
 		name string
@@ -25,7 +26,7 @@ func Test_groupResponseBySource(t *testing.T) {
 		tt := tt // Reinitialize for t.Parallel()
 		t.Run(tt.name, func(t *testing.T) {
 			t.Parallel()
-			if got := groupResponseBySource(tt.args.r, tt.args.query, tt.args.resp); !reflect.DeepEqual(got, tt.want) {
+			if got := groupResponseBySource(tt.args.r, tt.args.query, tt.args.resp, tt.args.callAnalysis); !reflect.DeepEqual(got, tt.want) {
 				t.Errorf("groupResponse() = %v, want %v", got, tt.want)
 			}
 		})