Repository: google/osv-scanner
Pull request #409 (2023-06-14 08:20:43)

Add option to include severity in table output
Add an optional `--include-severity` flag to include a severity column when using table or markdown output, when severity is present in the OSV schema.

giovanni-bozzano: Sure, test added.

From an end user perspective, it would be useful to also have the numeric score computed in the same column, for a quicker overview. Let me know if this sounds good.
another-rex: Thanks for working on this @giovanni-bozzano! 

Unfortunately we want to be very careful with what we add to the table output as we want to keep the horizontal width down, and also keep the number of CLI flags limited. 

Converting `CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L` into a single numeric score will help, but it also loses a decent amount of information. 

A more long term solution is to create a markdown/text based output format, which is more suitable for adding additional information like severity info, fixed versions...etc, We are currently developing this as part of #57, currently estimated to be completed this quarter. 

For now a workaround would be to parse the json output to get the severity value.
another-rex: Discussed this a bit more with the team, something similar to this could work showing only the computed scores in the table, and only showing the full string (or a subset of it) in the text output I was describing above so if the user wants more info they can get it. 

We can use https://github.com/goark/go-cvss to compute the score relatively easily, if you can replace the current full severity string with just the score it would be great!
oliverchang: > Thanks for the change! Agreed with adding to the documentation to clarify that this is the base severity score.
> 
> I think now that it's reasonably short, we can remove the `include-severity` flag and just always show the severity column. (Does mean a couple of the tests in main_test.go will need to be updated)

CC @hayleycd to see where we can fit this in our current docs. 
giovanni-bozzano: I removed the flag and renamed the column to "CVSS". Tests have been changed accordingly.
hayleycd: So what determines if the severity score is CVSS v2 or CVSS v3? (Clarifying so I can update the docs)
another-rex: Inside each OSV entry the severity looks like this:
```
  "severity": [
    {
      "type": "CVSS_V3",
      "score": "CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:H/I:H/A:H"
    }
  ],
```

The type field shows what version is used, currently the schema defines only two options, v3 and v2: https://ossf.github.io/osv-schema/#severity-field

As for why you would choose one over the other I think it's just newer advisories use cvss v3, and older ones use v2.

Patch:
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 4b1aa55..fdc7f90 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -40,11 +40,11 @@ jobs:
 
     steps:
       - name: Checkout repository
-        uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+        uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
 
       # Initializes the CodeQL tools for scanning.
       - name: Initialize CodeQL
-        uses: github/codeql-action/init@83f0fe6c4988d98a455712a27f0255212bba9bd4 # v2.3.6
+        uses: github/codeql-action/init@6c089f53dd51dc3fc7e599c3cb5356453a52ca9e # v2.20.0
         with:
           languages: ${{ matrix.language }}
           # If you wish to specify custom queries, you can do so here or in a config file.
@@ -55,7 +55,7 @@ jobs:
       # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
       # If this step fails, then you should remove it and run the build manually (see below)
       - name: Autobuild
-        uses: github/codeql-action/autobuild@83f0fe6c4988d98a455712a27f0255212bba9bd4 # v2.3.6
+        uses: github/codeql-action/autobuild@6c089f53dd51dc3fc7e599c3cb5356453a52ca9e # v2.20.0
 
       # ℹ️ Command-line programs to run using the OS shell.
       # 📚 https://git.io/JvXDl
@@ -69,4 +69,4 @@ jobs:
       #   make release
 
       - name: Perform CodeQL Analysis
-        uses: github/codeql-action/analyze@83f0fe6c4988d98a455712a27f0255212bba9bd4 # v2.3.6
+        uses: github/codeql-action/analyze@6c089f53dd51dc3fc7e599c3cb5356453a52ca9e # v2.20.0
diff --git a/.github/workflows/goreleaser.yml b/.github/workflows/goreleaser.yml
index 4329981..7b520ce 100644
--- a/.github/workflows/goreleaser.yml
+++ b/.github/workflows/goreleaser.yml
@@ -21,7 +21,7 @@ jobs:
       DOCKER_CLI_EXPERIMENTAL: "enabled"
     steps:
       - name: Checkout
-        uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+        uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
         with:
           fetch-depth: 0
       - name: Set up Go
@@ -33,17 +33,17 @@ jobs:
         uses: ./.github/workflows/test-action
       - name: Run Lints
         uses: ./.github/workflows/lint-action
-      - uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # v2
-      - uses: docker/setup-buildx-action@4b4e9c3e2d4531116a6f8ba8e71fc6e2cb6e6c8c # v2
+      - uses: docker/setup-qemu-action@2b82ce82d56a2a04d2637cd93a637ae1b359c0a7 # v2
+      - uses: docker/setup-buildx-action@ecf95283f03858871ff00b787d79c419715afc34 # v2
       - name: ghcr-login
-        uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a # v2
+        uses: docker/login-action@465a07811f14bebb1938fbed4728c6a1ff8901fc # v2
         with:
           registry: ghcr.io
           username: ${{ github.repository_owner }}
           password: ${{ secrets.GITHUB_TOKEN }}
       - name: Run GoReleaser
         id: run-goreleaser
-        uses: goreleaser/goreleaser-action@f82d6c1c344bcacabba2c841718984797f664a6b # v4.2.0
+        uses: goreleaser/goreleaser-action@336e29918d653399e599bfca99fadc1d7ffbc9f7 # v4.3.0
         with:
           version: latest
           args: release --rm-dist
@@ -63,7 +63,7 @@ jobs:
       actions: read # To read the workflow path.
       id-token: write # To sign the provenance.
       contents: write # To add assets to a release.
-    uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.6.0
+    uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.7.0
     with:
       base64-subjects: "${{ needs.goreleaser.outputs.hashes }}"
       upload-assets: true # upload to a new release
diff --git a/.github/workflows/lint-action/action.yml b/.github/workflows/lint-action/action.yml
index c3ffcf8..4153a88 100644
--- a/.github/workflows/lint-action/action.yml
+++ b/.github/workflows/lint-action/action.yml
@@ -19,7 +19,7 @@ runs:
   using: composite
   steps:
     - name: Run golangci-lint
-      uses: golangci/golangci-lint-action@5f1fec7010f6ae3b84ea4f7b2129beb8639b564f # v3.5.0
+      uses: golangci/golangci-lint-action@639cd343e1d3b897ff35927a75193d57cfcba299 # v3.6.0
       with:
         # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
         version: v1.51.1
diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml
index 6d19d23..f6fe300 100644
--- a/.github/workflows/lint.yaml
+++ b/.github/workflows/lint.yaml
@@ -32,7 +32,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Check out code
-        uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+        uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
         with:
           persist-credentials: false
           fetch-depth: 0
diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml
index 8dde34e..ff0800f 100644
--- a/.github/workflows/scorecards.yml
+++ b/.github/workflows/scorecards.yml
@@ -32,7 +32,7 @@ jobs:
 
     steps:
       - name: "Checkout code"
-        uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+        uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
         with:
           persist-credentials: false
 
@@ -67,6 +67,6 @@ jobs:
 
       # Upload the results to GitHub's code scanning dashboard.
       - name: "Upload to code-scanning"
-        uses: github/codeql-action/upload-sarif@83f0fe6c4988d98a455712a27f0255212bba9bd4 # v2.3.6
+        uses: github/codeql-action/upload-sarif@6c089f53dd51dc3fc7e599c3cb5356453a52ca9e # v2.20.0
         with:
           sarif_file: results.sarif
diff --git a/Dockerfile b/Dockerfile
index c4b4f48..1e84fe0 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-FROM golang:alpine@sha256:ee2f23f1a612da71b8a4cd78fec827f1e67b0a8546a98d257cca441a4ddbebcb
+FROM golang:alpine@sha256:fd9d9d7194ec40a9a6ae89fcaef3e47c47de7746dd5848ab5343695dbbd09f8c
 
 RUN mkdir /src
 WORKDIR /src
diff --git a/go.mod b/go.mod
index 7af8c28..2b8d94b 100644
--- a/go.mod
+++ b/go.mod
@@ -7,17 +7,18 @@ require (
 	github.com/CycloneDX/cyclonedx-go v0.7.1
 	github.com/go-git/go-billy/v5 v5.4.1
 	github.com/go-git/go-git/v5 v5.7.0
+	github.com/goark/go-cvss v1.6.6
 	github.com/google/go-cmp v0.5.9
 	github.com/jedib0t/go-pretty/v6 v6.4.6
 	github.com/kr/pretty v0.3.1
-	github.com/package-url/packageurl-go v0.1.0
+	github.com/package-url/packageurl-go v0.1.1
 	github.com/spdx/tools-golang v0.5.2
-	github.com/urfave/cli/v2 v2.25.6
+	github.com/urfave/cli/v2 v2.25.7
 	golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
 	golang.org/x/mod v0.11.0
 	golang.org/x/sync v0.3.0
 	golang.org/x/term v0.9.0
-	golang.org/x/tools v0.9.3
+	golang.org/x/tools v0.10.0
 	golang.org/x/vuln v0.0.0-20230303230808-d3042fecc4e3
 	gopkg.in/yaml.v3 v3.0.1
 )
@@ -31,6 +32,7 @@ require (
 	github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
 	github.com/emirpasic/gods v1.18.1 // indirect
 	github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
+	github.com/goark/errs v1.1.0 // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 	github.com/imdario/mergo v0.3.15 // indirect
 	github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
@@ -46,8 +48,8 @@ require (
 	github.com/spdx/gordf v0.0.0-20221230105357-b735bd5aac89 // 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.9.0 // indirect
-	golang.org/x/net v0.10.0 // indirect
+	golang.org/x/crypto v0.10.0 // indirect
+	golang.org/x/net v0.11.0 // indirect
 	golang.org/x/sys v0.9.0 // indirect
 	gopkg.in/warnings.v0 v0.1.2 // indirect
 )
diff --git a/go.sum b/go.sum
index 6291c65..41f9126 100644
--- a/go.sum
+++ b/go.sum
@@ -35,6 +35,10 @@ github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw4
 github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8=
 github.com/go-git/go-git/v5 v5.7.0 h1:t9AudWVLmqzlo+4bqdf7GY+46SUuRsx59SboFxkq2aE=
 github.com/go-git/go-git/v5 v5.7.0/go.mod h1:coJHKEOk5kUClpsNlXrUvPrDxY3w3gjHvhcZd8Fodw8=
+github.com/goark/errs v1.1.0 h1:FKnyw4LVyRADIjM8Nj0Up6r0/y5cfADvZAd1E+tthXE=
+github.com/goark/errs v1.1.0/go.mod h1:TtaPEoadm2mzqzfXdkkfpN2xuniCFm2q4JH+c1qzaqw=
+github.com/goark/go-cvss v1.6.6 h1:WJFuIWqmAw1Ilb9USv0vuX+nYzOWJp8lIujseJ/y3sU=
+github.com/goark/go-cvss v1.6.6/go.mod h1:H3qbfUSUlV7XtA3EwWNunvXz6OySwWHOuO+R6ZPMQPI=
 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
@@ -60,8 +64,8 @@ github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlW
 github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
 github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
-github.com/package-url/packageurl-go v0.1.0 h1:efWBc98O/dBZRg1pw2xiDzovnlMjCa9NPnfaiBduh8I=
-github.com/package-url/packageurl-go v0.1.0/go.mod h1:C/ApiuWpmbpni4DIOECf6WCjFUZV7O1Fx7VAzrZHgBw=
+github.com/package-url/packageurl-go v0.1.1 h1:KTRE0bK3sKbFKAk3yy63DpeskU7Cvs/x/Da5l+RtzyU=
+github.com/package-url/packageurl-go v0.1.1/go.mod h1:uQd4a7Rh3ZsVg5j0lNyAfyxIeGde9yrlhjF78GzeW0c=
 github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
 github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
 github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
@@ -96,8 +100,8 @@ github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
 github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/urfave/cli/v2 v2.25.6 h1:yuSkgDSZfH3L1CjF2/5fNNg2KbM47pY2EvjBq4ESQnU=
-github.com/urfave/cli/v2 v2.25.6/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
+github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
+github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
 github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
 github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
 github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
@@ -107,8 +111,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
-golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
-golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
+golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
+golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
 golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
 golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
@@ -121,8 +125,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
-golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
-golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
+golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
+golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
 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/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -156,13 +160,13 @@ golang.org/x/text v0.3.6/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/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
-golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
+golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
 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.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
-golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM=
-golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
+golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg=
+golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM=
 golang.org/x/vuln v0.0.0-20230303230808-d3042fecc4e3 h1:9GJsAwSzB/ztwMwsEm3ihUgCXHCULbNsubxqIrdKa44=
 golang.org/x/vuln v0.0.0-20230303230808-d3042fecc4e3/go.mod h1:LTLnfk/dpXDNKsX6aCg/cI4LyCVnTyrQhgV/yLJuly0=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/internal/output/markdowntable.go b/internal/output/markdowntable.go
index c141d4f..ac69b7a 100644
--- a/internal/output/markdowntable.go
+++ b/internal/output/markdowntable.go
@@ -12,7 +12,7 @@ import (
 func PrintMarkdownTableResults(vulnResult *models.VulnerabilityResults, outputWriter io.Writer) {
 	outputTable := table.NewWriter()
 	outputTable.SetOutputMirror(outputWriter)
-	outputTable.AppendHeader(table.Row{"OSV URL", "Ecosystem", "Package", "Version", "Source"})
+	outputTable.AppendHeader(table.Row{"OSV URL", "CVSS", "Ecosystem", "Package", "Version", "Source"})
 
 	outputTable = tableBuilder(outputTable, vulnResult, false)
 
diff --git a/internal/output/table.go b/internal/output/table.go
index c363a0b..18519b1 100644
--- a/internal/output/table.go
+++ b/internal/output/table.go
@@ -1,11 +1,15 @@
 package output
 
 import (
+	"fmt"
 	"io"
 	"os"
 	"path/filepath"
 	"strings"
 
+	v2_metric "github.com/goark/go-cvss/v2/metric"
+	v3_metric "github.com/goark/go-cvss/v3/metric"
+
 	"github.com/google/osv-scanner/pkg/models"
 	"github.com/google/osv-scanner/pkg/osv"
 
@@ -18,7 +22,7 @@ import (
 func PrintTableResults(vulnResult *models.VulnerabilityResults, outputWriter io.Writer) {
 	outputTable := table.NewWriter()
 	outputTable.SetOutputMirror(outputWriter)
-	outputTable.AppendHeader(table.Row{"OSV URL (ID In Bold)", "Ecosystem", "Package", "Version", "Source"})
+	outputTable.AppendHeader(table.Row{"OSV URL", "CVSS", "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
@@ -100,6 +104,32 @@ func tableBuilderInner(vulnResult *models.VulnerabilityResults, addStyling bool,
 
 				outputRow = append(outputRow, strings.Join(links, "\n"))
 
+				var outputSeverities []string
+				for _, vulnID := range group.IDs {
+					var severities []models.Severity
+					for _, vuln := range pkg.Vulnerabilities {
+						if vuln.ID == vulnID {
+							severities = vuln.Severity
+						}
+					}
+					for _, severity := range severities {
+						var outputSeverity string
+						switch severity.Type {
+						case models.SeverityCVSSV2:
+							numericSeverity, _ := v2_metric.NewBase().Decode(severity.Score)
+							outputSeverity = fmt.Sprintf("%v", numericSeverity.Score())
+						case models.SeverityCVSSV3:
+							numericSeverity, _ := v3_metric.NewBase().Decode(severity.Score)
+							outputSeverity = fmt.Sprintf("%v", numericSeverity.Score())
+						default:
+							outputSeverity = severity.Score
+						}
+
+						outputSeverities = append(outputSeverities, outputSeverity)
+					}
+				}
+				outputRow = append(outputRow, strings.Join(outputSeverities, ",\n"))
+
 				if pkg.Package.Ecosystem == "GIT" {
 					outputRow = append(outputRow, "GIT", pkg.Package.Version, pkg.Package.Version)
 					shouldMerge = true
diff --git a/pkg/models/constants.go b/pkg/models/constants.go
index 9915c4a..246fa5e 100644
--- a/pkg/models/constants.go
+++ b/pkg/models/constants.go
@@ -22,6 +22,7 @@ const (
 	EcosystemConanCenter   Ecosystem = "ConanCenter"
 	EcosystemRockyLinux    Ecosystem = "Rocky Linux"
 	EcosystemAlmaLinux     Ecosystem = "AlmaLinux"
+	EcosystemBitnami       Ecosystem = "Bitnami"
 )
 
 type SeverityType string
diff --git a/pkg/models/vulnerability.go b/pkg/models/vulnerability.go
index 335a32c..56b30bf 100644
--- a/pkg/models/vulnerability.go
+++ b/pkg/models/vulnerability.go
@@ -1,15 +1,18 @@
 package models
 
-import "time"
+import (
+	"encoding/json"
+	"time"
+)
 
 // Package identifies the affected code library or command provided by the
 // package.
 //
 // See: https://ossf.github.io/osv-schema/#affectedpackage-field
 type Package struct {
-	Ecosystem Ecosystem `json:"ecosystem,omitempty"`
-	Name      string    `json:"name,omitempty"`
-	Purl      string    `json:"purl,omitempty"`
+	Ecosystem Ecosystem `json:"ecosystem" yaml:"ecosystem"`
+	Name      string    `json:"name" yaml:"name"`
+	Purl      string    `json:"purl,omitempty" yaml:"purl,omitempty"`
 }
 
 // Event describes a single version that either:
@@ -24,19 +27,20 @@ type Package struct {
 //
 // See: https://ossf.github.io/osv-schema/#affectedrangesevents-fields
 type Event struct {
-	Introduced   string `json:"introduced,omitempty"`
-	Fixed        string `json:"fixed,omitempty"`
-	LastAffected string `json:"last_affected,omitempty"`
-	Limit        string `json:"limit,omitempty"`
+	Introduced   string `json:"introduced,omitempty" yaml:"introduced,omitempty"`
+	Fixed        string `json:"fixed,omitempty" yaml:"fixed,omitempty"`
+	LastAffected string `json:"last_affected,omitempty" yaml:"last_affected,omitempty"`
+	Limit        string `json:"limit,omitempty" yaml:"limit,omitempty"`
 }
 
 // Range describes the affected range of given version for a specific package.
 //
 // See: https://ossf.github.io/osv-schema/#affectedranges-field
 type Range struct {
-	Type             RangeType              `json:"type"`
-	Events           []Event                `json:"events"`
-	DatabaseSpecific map[string]interface{} `json:"database_specific,omitempty"`
+	Type             RangeType              `json:"type" yaml:"type"`
+	Events           []Event                `json:"events" yaml:"events"`
+	Repo             string                 `json:"repo,omitempty" yaml:"repo,omitempty"`
+	DatabaseSpecific map[string]interface{} `json:"database_specific,omitempty" yaml:"database_specific,omitempty"`
 }
 
 // Severity is used to describe the severity of a vulnerability for an affected
@@ -44,8 +48,8 @@ type Range struct {
 //
 // See: https://ossf.github.io/osv-schema/#severity-field
 type Severity struct {
-	Type  SeverityType `json:"type"`
-	Score string       `json:"score"`
+	Type  SeverityType `json:"type" yaml:"type"`
+	Score string       `json:"score" yaml:"score"`
 }
 
 // Affected describes an affected package version, meaning one instance that
@@ -53,12 +57,33 @@ type Severity struct {
 //
 // See: https://ossf.github.io/osv-schema/#affected-fields
 type Affected struct {
-	Package           Package                `json:"package"`
-	Severity          []Severity             `json:"severity,omitempty"`
-	Ranges            []Range                `json:"ranges,omitempty"`
-	Versions          []string               `json:"versions,omitempty"`
-	DatabaseSpecific  map[string]interface{} `json:"database_specific,omitempty"`
-	EcosystemSpecific map[string]interface{} `json:"ecosystem_specific,omitempty"`
+	Package           Package                `json:"package,omitempty" yaml:"package,omitempty"`
+	Severity          []Severity             `json:"severity,omitempty" yaml:"severity,omitempty"`
+	Ranges            []Range                `json:"ranges,omitempty" yaml:"ranges,omitempty"`
+	Versions          []string               `json:"versions,omitempty" yaml:"versions,omitempty"`
+	DatabaseSpecific  map[string]interface{} `json:"database_specific,omitempty" yaml:"database_specific,omitempty"`
+	EcosystemSpecific map[string]interface{} `json:"ecosystem_specific,omitempty" yaml:"ecosystem_specific,omitempty"`
+}
+
+// MarshalJSON implements the json.Marshaler interface.
+//
+// This method ensures Package is only present if it is not equal to the zero value.
+// This is achieved by embedding the Affected struct with a pointer to Package used
+// to populate the "package" key in the JSON object.
+func (a Affected) MarshalJSON() ([]byte, error) {
+	type rawAffected Affected // alias Affected to avoid recursion during Marshal
+	type wrapper struct {
+		Package *Package `json:"package,omitempty" yaml:"package,omitempty"`
+		rawAffected
+	}
+	raw := wrapper{rawAffected: rawAffected(a)}
+	if a.Package == (Package{}) {
+		raw.Package = nil
+	} else {
+		raw.Package = &(a.Package)
+	}
+
+	return json.Marshal(raw)
 }
 
 // Reference links to additional information, advisories, issue tracker entries,
@@ -66,8 +91,8 @@ type Affected struct {
 //
 // See: https://ossf.github.io/osv-schema/#references-field
 type Reference struct {
-	Type ReferenceType `json:"type"`
-	URL  string        `json:"url"`
+	Type ReferenceType `json:"type" yaml:"type"`
+	URL  string        `json:"url" yaml:"url"`
 }
 
 // Credit gives credit for the discovery, confirmation, patch, or other events
@@ -75,9 +100,9 @@ type Reference struct {
 //
 // See: https://ossf.github.io/osv-schema/#credits-fields
 type Credit struct {
-	Name    string     `json:"name"`
-	Contact []string   `json:"contact,omitempty"`
-	Type    CreditType `json:"type"`
+	Name    string     `json:"name" yaml:"name"`
+	Type    CreditType `json:"type,omitempty" yaml:"type,omitempty"`
+	Contact []string   `json:"contact,omitempty" yaml:"contact,omitempty"`
 }
 
 // Vulnerability is the core Open Source Vulnerability (OSV) data type.
@@ -85,17 +110,17 @@ type Credit struct {
 // The full documentation for the schema is available at
 // https://ossf.github.io/osv-schema.
 type Vulnerability struct {
-	SchemaVersion    string                 `json:"schema_version"`
-	ID               string                 `json:"id"`
-	Modified         time.Time              `json:"modified"`
-	Published        time.Time              `json:"published"`
-	Aliases          []string               `json:"aliases,omitempty"`
-	Related          []string               `json:"related,omitempty"`
-	Summary          string                 `json:"summary"`
-	Details          string                 `json:"details"`
-	Affected         []Affected             `json:"affected"`
-	Severity         []Severity             `json:"severity,omitempty"`
-	References       []Reference            `json:"references,omitempty"`
-	Credits          []Credit               `json:"credits,omitempty"`
-	DatabaseSpecific map[string]interface{} `json:"database_specific,omitempty"`
+	SchemaVersion    string                 `json:"schema_version,omitempty" yaml:"schema_version,omitempty"`
+	ID               string                 `json:"id" yaml:"id"`
+	Modified         time.Time              `json:"modified" yaml:"modified"`
+	Published        time.Time              `json:"published,omitempty" yaml:"published,omitempty"`
+	Aliases          []string               `json:"aliases,omitempty" yaml:"aliases,omitempty"`
+	Related          []string               `json:"related,omitempty" yaml:"related,omitempty"`
+	Summary          string                 `json:"summary,omitempty" yaml:"summary,omitempty"`
+	Details          string                 `json:"details,omitempty" yaml:"details,omitempty"`
+	Affected         []Affected             `json:"affected,omitempty" yaml:"affected,omitempty"`
+	Severity         []Severity             `json:"severity,omitempty" yaml:"severity,omitempty"`
+	References       []Reference            `json:"references,omitempty" yaml:"references,omitempty"`
+	Credits          []Credit               `json:"credits,omitempty" yaml:"credits,omitempty"`
+	DatabaseSpecific map[string]interface{} `json:"database_specific,omitempty" yaml:"database_specific,omitempty"`
 }


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 6673f4d..fbfb63c 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -32,7 +32,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Check out code
-        uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+        uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
         with:
           persist-credentials: false
           fetch-depth: 0
diff --git a/cmd/osv-scanner/main_test.go b/cmd/osv-scanner/main_test.go
index 978ddc4..b199fb0 100644
--- a/cmd/osv-scanner/main_test.go
+++ b/cmd/osv-scanner/main_test.go
@@ -144,18 +144,18 @@ func TestRun(t *testing.T) {
 			wantStdout: `
 				Scanning dir ./fixtures/sbom-insecure/postgres-stretch.cdx.xml
 				Scanned %%/fixtures/sbom-insecure/postgres-stretch.cdx.xml as CycloneDX SBOM and found 136 packages
-				+-------------------------------------+-----------+---------+------------------------------------+-------------------------------------------------+
-				| OSV URL (ID IN BOLD)                | ECOSYSTEM | PACKAGE | VERSION                            | SOURCE                                          |
-				+-------------------------------------+-----------+---------+------------------------------------+-------------------------------------------------+
-				| https://osv.dev/GHSA-v95c-p5hm-xq8f | Go        | runc    | v1.0.1                             | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
-				| https://osv.dev/GO-2022-0274        |           |         |                                    |                                                 |
-				| https://osv.dev/GHSA-f3fp-gc8g-vw66 | Go        | runc    | v1.0.1                             | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
-				| https://osv.dev/GHSA-g2j6-57v7-gm8c | Go        | runc    | v1.0.1                             | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
-				| https://osv.dev/GHSA-m8cg-xc2p-r3fc | Go        | runc    | v1.0.1                             | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
-				| https://osv.dev/GHSA-vpvm-3wq2-2wvm | Go        | runc    | v1.0.1                             | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
-				| https://osv.dev/GHSA-p782-xgp4-8hr8 | Go        | sys     | v0.0.0-20210817142637-7d9622a276b7 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
-				| https://osv.dev/GO-2022-0493        |           |         |                                    |                                                 |
-				+-------------------------------------+-----------+---------+------------------------------------+-------------------------------------------------+
+				+-------------------------------------+------+-----------+---------+------------------------------------+-------------------------------------------------+
+				| OSV URL                             | CVSS | ECOSYSTEM | PACKAGE | VERSION                            | SOURCE                                          |
+				+-------------------------------------+------+-----------+---------+------------------------------------+-------------------------------------------------+
+				| https://osv.dev/GHSA-v95c-p5hm-xq8f | 6    | Go        | runc    | v1.0.1                             | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
+				| https://osv.dev/GO-2022-0274        |      |           |         |                                    |                                                 |
+				| https://osv.dev/GHSA-f3fp-gc8g-vw66 | 5.9  | Go        | runc    | v1.0.1                             | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
+				| https://osv.dev/GHSA-g2j6-57v7-gm8c | 6.1  | Go        | runc    | v1.0.1                             | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
+				| https://osv.dev/GHSA-m8cg-xc2p-r3fc | 2.5  | Go        | runc    | v1.0.1                             | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
+				| https://osv.dev/GHSA-vpvm-3wq2-2wvm | 7    | Go        | runc    | v1.0.1                             | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
+				| https://osv.dev/GHSA-p782-xgp4-8hr8 | 5.3  | Go        | sys     | v0.0.0-20210817142637-7d9622a276b7 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
+				| https://osv.dev/GO-2022-0493        |      |           |         |                                    |                                                 |
+				+-------------------------------------+------+-----------+---------+------------------------------------+-------------------------------------------------+
 			`,
 			wantStderr: "",
 		},
@@ -379,11 +379,11 @@ func TestRun_LockfileWithExplicitParseAs(t *testing.T) {
 				Scanned %%/fixtures/locks-insecure/my-package-lock.json file as a package-lock.json and found 1 packages
 				Scanning dir ./fixtures/locks-insecure
 				Scanned %%/fixtures/locks-insecure/composer.lock file and found 0 packages
-				+-------------------------------------+-----------+-----------+---------+----------------------------------------------+
-				| OSV URL (ID IN BOLD)                | ECOSYSTEM | PACKAGE   | VERSION | SOURCE                                       |
-				+-------------------------------------+-----------+-----------+---------+----------------------------------------------+
-				| https://osv.dev/GHSA-whgm-jr23-g3j9 | npm       | ansi-html | 0.0.1   | fixtures/locks-insecure/my-package-lock.json |
-				+-------------------------------------+-----------+-----------+---------+----------------------------------------------+
+				+-------------------------------------+------+-----------+-----------+---------+----------------------------------------------+
+				| OSV URL                             | CVSS | ECOSYSTEM | PACKAGE   | VERSION | SOURCE                                       |
+				+-------------------------------------+------+-----------+-----------+---------+----------------------------------------------+
+				| https://osv.dev/GHSA-whgm-jr23-g3j9 | 7.5  | npm       | ansi-html | 0.0.1   | fixtures/locks-insecure/my-package-lock.json |
+				+-------------------------------------+------+-----------+-----------+---------+----------------------------------------------+
 			`,
 			wantStderr: "",
 		},
@@ -402,12 +402,12 @@ func TestRun_LockfileWithExplicitParseAs(t *testing.T) {
 				Scanned %%/fixtures/locks-insecure/my-yarn.lock file as a yarn.lock and found 1 packages
 				Scanning dir ./fixtures/locks-insecure
 				Scanned %%/fixtures/locks-insecure/composer.lock file and found 0 packages
-				+-------------------------------------+-----------+-----------+---------+----------------------------------------------+
-				| OSV URL (ID IN BOLD)                | ECOSYSTEM | PACKAGE   | VERSION | SOURCE                                       |
-				+-------------------------------------+-----------+-----------+---------+----------------------------------------------+
-				| https://osv.dev/GHSA-whgm-jr23-g3j9 | npm       | ansi-html | 0.0.1   | fixtures/locks-insecure/my-package-lock.json |
-				| https://osv.dev/GHSA-whgm-jr23-g3j9 | npm       | ansi-html | 0.0.1   | fixtures/locks-insecure/my-yarn.lock         |
-				+-------------------------------------+-----------+-----------+---------+----------------------------------------------+
+				+-------------------------------------+------+-----------+-----------+---------+----------------------------------------------+
+				| OSV URL                             | CVSS | ECOSYSTEM | PACKAGE   | VERSION | SOURCE                                       |
+				+-------------------------------------+------+-----------+-----------+---------+----------------------------------------------+
+				| https://osv.dev/GHSA-whgm-jr23-g3j9 | 7.5  | npm       | ansi-html | 0.0.1   | fixtures/locks-insecure/my-package-lock.json |
+				| https://osv.dev/GHSA-whgm-jr23-g3j9 | 7.5  | npm       | ansi-html | 0.0.1   | fixtures/locks-insecure/my-yarn.lock         |
+				+-------------------------------------+------+-----------+-----------+---------+----------------------------------------------+
 			`,
 			wantStderr: "",
 		},
@@ -425,12 +425,12 @@ func TestRun_LockfileWithExplicitParseAs(t *testing.T) {
 				Scanned %%/fixtures/locks-insecure/my-package-lock.json file as a package-lock.json and found 1 packages
 				Scanning dir ./fixtures/locks-insecure
 				Scanned %%/fixtures/locks-insecure/composer.lock file and found 0 packages
-				+-------------------------------------+-----------+-----------+---------+----------------------------------------------+
-				| OSV URL (ID IN BOLD)                | ECOSYSTEM | PACKAGE   | VERSION | SOURCE                                       |
-				+-------------------------------------+-----------+-----------+---------+----------------------------------------------+
-				| https://osv.dev/GHSA-whgm-jr23-g3j9 | npm       | ansi-html | 0.0.1   | fixtures/locks-insecure/my-package-lock.json |
-				| https://osv.dev/GHSA-whgm-jr23-g3j9 | npm       | ansi-html | 0.0.1   | fixtures/locks-insecure/my-yarn.lock         |
-				+-------------------------------------+-----------+-----------+---------+----------------------------------------------+
+				+-------------------------------------+------+-----------+-----------+---------+----------------------------------------------+
+				| OSV URL                             | CVSS | ECOSYSTEM | PACKAGE   | VERSION | SOURCE                                       |
+				+-------------------------------------+------+-----------+-----------+---------+----------------------------------------------+
+				| https://osv.dev/GHSA-whgm-jr23-g3j9 | 7.5  | npm       | ansi-html | 0.0.1   | fixtures/locks-insecure/my-package-lock.json |
+				| https://osv.dev/GHSA-whgm-jr23-g3j9 | 7.5  | npm       | ansi-html | 0.0.1   | fixtures/locks-insecure/my-yarn.lock         |
+				+-------------------------------------+------+-----------+-----------+---------+----------------------------------------------+
 			`,
 			wantStderr: "",
 		},
diff --git a/pkg/models/vulnerability_test.go b/pkg/models/vulnerability_test.go
new file mode 100644
index 0000000..b6cb7d8
--- /dev/null
+++ b/pkg/models/vulnerability_test.go
@@ -0,0 +1,103 @@
+package models
+
+import (
+	"encoding/json"
+	"testing"
+
+	"gopkg.in/yaml.v3"
+)
+
+func TestAffected_MarshalJSONWithPackage(t *testing.T) {
+	t.Parallel()
+	v := Vulnerability{
+		ID: "TEST-0000",
+		Affected: []Affected{
+			{
+				Package:  Package{Ecosystem: EcosystemPyPI, Name: "requests"},
+				Versions: []string{"1.0.0"},
+			},
+		},
+	}
+	got, err := json.Marshal(v)
+	if err != nil {
+		t.Fatalf("Marshal() = %v; want no error", err)
+	}
+	want := `{"id":"TEST-0000","modified":"0001-01-01T00:00:00Z","published":"0001-01-01T00:00:00Z","affected":[{"package":{"ecosystem":"PyPI","name":"requests"},"versions":["1.0.0"]}]}`
+	if string(got) != want {
+		t.Errorf("Marshal() = %v; want %v", string(got), want)
+	}
+}
+
+func TestAffected_MarshalJSONWithoutPackage(t *testing.T) {
+	t.Parallel()
+	v := Vulnerability{
+		ID: "TEST-0000",
+		Affected: []Affected{
+			{
+				Versions: []string{"1.0.0"},
+			},
+		},
+	}
+	got, err := json.Marshal(v)
+	if err != nil {
+		t.Fatalf("Marshal() = %v; want no error", err)
+	}
+	want := `{"id":"TEST-0000","modified":"0001-01-01T00:00:00Z","published":"0001-01-01T00:00:00Z","affected":[{"versions":["1.0.0"]}]}`
+	if string(got) != want {
+		t.Errorf("Marshal() = %v; want %v", string(got), want)
+	}
+}
+
+func TestAffected_MarshalYAMLWithPackage(t *testing.T) {
+	t.Parallel()
+	v := Vulnerability{
+		ID: "TEST-0000",
+		Affected: []Affected{
+			{
+				Package:  Package{Ecosystem: EcosystemPyPI, Name: "requests"},
+				Versions: []string{"1.0.0"},
+			},
+		},
+	}
+	got, err := yaml.Marshal(v)
+	if err != nil {
+		t.Fatalf("Marshal() = %v; want no error", err)
+	}
+	want := `id: TEST-0000
+modified: 0001-01-01T00:00:00Z
+affected:
+    - package:
+        ecosystem: PyPI
+        name: requests
+      versions:
+        - 1.0.0
+`
+	if string(got) != want {
+		t.Errorf("Marshal() = %v; want %v", string(got), want)
+	}
+}
+
+func TestAffected_MarshalYAMLWithoutPackage(t *testing.T) {
+	t.Parallel()
+	v := Vulnerability{
+		ID: "TEST-0000",
+		Affected: []Affected{
+			{
+				Versions: []string{"1.0.0"},
+			},
+		},
+	}
+	got, err := yaml.Marshal(v)
+	if err != nil {
+		t.Fatalf("Marshal() = %v; want no error", err)
+	}
+	want := `id: TEST-0000
+modified: 0001-01-01T00:00:00Z
+affected:
+    - versions:
+        - 1.0.0
+`
+	if string(got) != want {
+		t.Errorf("Marshal() = %v; want %v", string(got), want)
+	}
+}