internal/report: update wording of lint errors to add more context

Adds additional context about where a lint error was found, and unifies
wording of similar lint messages.

Change-Id: I9e9b4132e1c5b20061b9ccbdc70ecd989abe6b8a
Reviewed-on: https://go-review.googlesource.com/c/vulndb/+/543396
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/internal/genericosv/testdata/yaml/GHSA-33m6-q9v5-62r7.yaml b/internal/genericosv/testdata/yaml/GHSA-33m6-q9v5-62r7.yaml
index fc27b37..6d94d0b 100644
--- a/internal/genericosv/testdata/yaml/GHSA-33m6-q9v5-62r7.yaml
+++ b/internal/genericosv/testdata/yaml/GHSA-33m6-q9v5-62r7.yaml
@@ -55,7 +55,7 @@
     - web: https://bugzilla.redhat.com/show_bug.cgi?id=1954376
     - web: https://snyk.io/vuln/SNYK-GOLANG-GITHUBCOMSATORIGOUUID-72488
 notes:
-    - lint: 'github.com/apptainer/sif: 2 versions do not exist: 1.2.1-0.20180103161547-0ef6afb2f6cd, 1.2.1-0.20180404165556-75cca531ea76'
-    - lint: 'github.com/satori/go.uuid: vulnerable_at version 1.2.0 is not inside vulnerable range'
-    - lint: references should contain at most one advisory link
-    - lint: summary should begin with a capital letter
+    - lint: 'modules[0] "github.com/apptainer/sif": 2 versions do not exist: 1.2.1-0.20180103161547-0ef6afb2f6cd, 1.2.1-0.20180404165556-75cca531ea76'
+    - lint: 'modules[1] "github.com/satori/go.uuid": vulnerable_at: 1.2.0 is not inside vulnerable range'
+    - lint: 'references: too many advisories (found 2, want <=1)'
+    - lint: 'summary: must begin with a capital letter'
diff --git a/internal/genericosv/testdata/yaml/GHSA-3hwm-922r-47hw.yaml b/internal/genericosv/testdata/yaml/GHSA-3hwm-922r-47hw.yaml
index dea330e..c5289f1 100644
--- a/internal/genericosv/testdata/yaml/GHSA-3hwm-922r-47hw.yaml
+++ b/internal/genericosv/testdata/yaml/GHSA-3hwm-922r-47hw.yaml
@@ -21,4 +21,4 @@
     - web: https://github.com/42Atomys/stud42/issues/412
     - web: https://github.com/42Atomys/stud42/commit/a70bfc72fba721917bf681d72a58093fb9deee17
 notes:
-    - lint: 'atomys.codes/stud42: version 0.23.0 does not exist'
+    - lint: 'modules[0] "atomys.codes/stud42": version 0.23.0 does not exist'
diff --git a/internal/genericosv/testdata/yaml/GHSA-3wq5-3f56-v5xc.yaml b/internal/genericosv/testdata/yaml/GHSA-3wq5-3f56-v5xc.yaml
index 9185771..a5fbd0b 100644
--- a/internal/genericosv/testdata/yaml/GHSA-3wq5-3f56-v5xc.yaml
+++ b/internal/genericosv/testdata/yaml/GHSA-3wq5-3f56-v5xc.yaml
@@ -25,5 +25,5 @@
     - advisory: https://nvd.nist.gov/vuln/detail/CVE-2023-1777
     - web: https://mattermost.com/security-updates/
 notes:
-    - lint: 'github.com/mattermost/mattermost-server/v6: version 7.1.6 does not exist'
-    - lint: 'github.com/mattermost/mattermost-server: 6 versions do not exist: 7.1.0, 7.1.6, 7.7.0, 7.7.2, 7.8.0, 7.8.1'
+    - lint: 'modules[0] "github.com/mattermost/mattermost-server": 6 versions do not exist: 7.1.0, 7.1.6, 7.7.0, 7.7.2, 7.8.0, 7.8.1'
+    - lint: 'modules[1] "github.com/mattermost/mattermost-server/v6": version 7.1.6 does not exist'
diff --git a/internal/genericosv/testdata/yaml/GHSA-54q4-74p3-mgcw.yaml b/internal/genericosv/testdata/yaml/GHSA-54q4-74p3-mgcw.yaml
index 5f930cc..f9586f7 100644
--- a/internal/genericosv/testdata/yaml/GHSA-54q4-74p3-mgcw.yaml
+++ b/internal/genericosv/testdata/yaml/GHSA-54q4-74p3-mgcw.yaml
@@ -18,6 +18,6 @@
     - advisory: https://nvd.nist.gov/vuln/detail/CVE-2022-38867
     - report: https://github.com/zhaojh329/rttys/issues/117
 notes:
-    - lint: 'github.com/zhaojh329/rttys: version 4.0.0 does not exist'
-    - lint: 'github.com/zhaojh329/rttys: version issue: 1 unsupported version(s)'
-    - lint: summary should begin with a capital letter
+    - lint: 'modules[0] "github.com/zhaojh329/rttys": unsupported_versions: found 1 (want none)'
+    - lint: 'modules[0] "github.com/zhaojh329/rttys": version 4.0.0 does not exist'
+    - lint: 'summary: must begin with a capital letter'
diff --git a/internal/genericosv/testdata/yaml/GHSA-5m6c-jp6f-2vcv.yaml b/internal/genericosv/testdata/yaml/GHSA-5m6c-jp6f-2vcv.yaml
index ebf3e92..632e7b4 100644
--- a/internal/genericosv/testdata/yaml/GHSA-5m6c-jp6f-2vcv.yaml
+++ b/internal/genericosv/testdata/yaml/GHSA-5m6c-jp6f-2vcv.yaml
@@ -21,5 +21,5 @@
     - fix: https://github.com/oauth2-proxy/oauth2-proxy/commit/ee5662e0f5001d76ec76562bb605abbd07c266a2
     - web: https://github.com/oauth2-proxy/oauth2-proxy/releases/tag/v6.0.0
 notes:
-    - lint: 'github.com/oauth2-proxy/oauth2-proxy: 2 versions do not exist: 5.1.1, 6.0.0'
-    - lint: references should contain at most one advisory link
+    - lint: 'modules[0] "github.com/oauth2-proxy/oauth2-proxy": 2 versions do not exist: 5.1.1, 6.0.0'
+    - lint: 'references: too many advisories (found 2, want <=1)'
diff --git a/internal/genericosv/testdata/yaml/GHSA-627p-rr78-99rj.yaml b/internal/genericosv/testdata/yaml/GHSA-627p-rr78-99rj.yaml
index 461de38..288fb61 100644
--- a/internal/genericosv/testdata/yaml/GHSA-627p-rr78-99rj.yaml
+++ b/internal/genericosv/testdata/yaml/GHSA-627p-rr78-99rj.yaml
@@ -66,6 +66,6 @@
     - advisory: https://nvd.nist.gov/vuln/detail/CVE-2020-5415
     - web: https://tanzu.vmware.com/security/cve-2020-5415
 notes:
-    - lint: 'github.com/concourse/concourse: 4 versions do not exist: 6.3.0, 6.3.1, 6.4.0, 6.4.1'
-    - lint: 'github.com/concourse/dex: 4 versions do not exist: 6.3.0, 6.3.1, 6.4.0, 6.4.1'
-    - lint: references should contain at most one advisory link
+    - lint: 'modules[0] "github.com/concourse/concourse": 4 versions do not exist: 6.3.0, 6.3.1, 6.4.0, 6.4.1'
+    - lint: 'modules[1] "github.com/concourse/dex": 4 versions do not exist: 6.3.0, 6.3.1, 6.4.0, 6.4.1'
+    - lint: 'references: too many advisories (found 2, want <=1)'
diff --git a/internal/genericosv/testdata/yaml/GHSA-66p8-j459-rq63.yaml b/internal/genericosv/testdata/yaml/GHSA-66p8-j459-rq63.yaml
index 62371dd..ade0fca 100644
--- a/internal/genericosv/testdata/yaml/GHSA-66p8-j459-rq63.yaml
+++ b/internal/genericosv/testdata/yaml/GHSA-66p8-j459-rq63.yaml
@@ -43,5 +43,5 @@
     - advisory: https://nvd.nist.gov/vuln/detail/CVE-2023-25168
     - fix: https://github.com/pterodactyl/wings/commit/429ac62dba22997a278bc709df5ac00a5a25d83d
 notes:
-    - lint: references should contain at most one advisory link
-    - lint: 'summary is too long: 131 characters (max 100)'
+    - lint: 'references: too many advisories (found 2, want <=1)'
+    - lint: 'summary: too long (found 131 characters, want <=100)'
diff --git a/internal/genericosv/testdata/yaml/GHSA-69v6-xc2j-r2jf.yaml b/internal/genericosv/testdata/yaml/GHSA-69v6-xc2j-r2jf.yaml
index 3fbb20e..2af66be 100644
--- a/internal/genericosv/testdata/yaml/GHSA-69v6-xc2j-r2jf.yaml
+++ b/internal/genericosv/testdata/yaml/GHSA-69v6-xc2j-r2jf.yaml
@@ -40,6 +40,6 @@
     - fix: https://github.com/ethereum/go-ethereum/commit/295693759e5ded05fec0b2fb39359965b60da785
     - web: https://blog.ethereum.org/2020/11/12/geth_security_release/
 notes:
-    - lint: 'github.com/ethereum/go-ethereum: missing skip_fix and vulnerable_at: "github.com/ethereum/go-ethereum/core/vm"'
-    - lint: 'github.com/ethereum/go-ethereum: version 1.19.7 does not exist'
-    - lint: references should contain at most one advisory link
+    - lint: 'modules[1] "github.com/ethereum/go-ethereum": packages[0] "github.com/ethereum/go-ethereum/core/vm": at least one of vulnerable_at and skip_fix must be set'
+    - lint: 'modules[1] "github.com/ethereum/go-ethereum": version 1.19.7 does not exist'
+    - lint: 'references: too many advisories (found 2, want <=1)'
diff --git a/internal/genericosv/testdata/yaml/GHSA-6rg3-8h8x-5xfv.yaml b/internal/genericosv/testdata/yaml/GHSA-6rg3-8h8x-5xfv.yaml
index 64ab431..f7e83fd 100644
--- a/internal/genericosv/testdata/yaml/GHSA-6rg3-8h8x-5xfv.yaml
+++ b/internal/genericosv/testdata/yaml/GHSA-6rg3-8h8x-5xfv.yaml
@@ -28,4 +28,4 @@
 references:
     - advisory: https://github.com/pterodactyl/wings/security/advisories/GHSA-6rg3-8h8x-5xfv
 notes:
-    - lint: 'summary is too long: 110 characters (max 100)'
+    - lint: 'summary: too long (found 110 characters, want <=100)'
diff --git a/internal/genericosv/testdata/yaml/GHSA-7943-82jg-wmw5.yaml b/internal/genericosv/testdata/yaml/GHSA-7943-82jg-wmw5.yaml
index ae7c6e3..bbbbac0 100644
--- a/internal/genericosv/testdata/yaml/GHSA-7943-82jg-wmw5.yaml
+++ b/internal/genericosv/testdata/yaml/GHSA-7943-82jg-wmw5.yaml
@@ -128,5 +128,5 @@
     - web: https://github.com/argoproj/argo-cd/releases/tag/v2.3.6
     - web: https://github.com/argoproj/argo-cd/releases/tag/v2.4.5
 notes:
-    - lint: 'github.com/argoproj/argo-cd: version 2.2.11 does not exist'
-    - lint: references should contain at most one advisory link
+    - lint: 'modules[0] "github.com/argoproj/argo-cd": version 2.2.11 does not exist'
+    - lint: 'references: too many advisories (found 2, want <=1)'
diff --git a/internal/genericosv/testdata/yaml/GHSA-7fxj-fr3v-r9gj.yaml b/internal/genericosv/testdata/yaml/GHSA-7fxj-fr3v-r9gj.yaml
index 8ad056d..5d4c177 100644
--- a/internal/genericosv/testdata/yaml/GHSA-7fxj-fr3v-r9gj.yaml
+++ b/internal/genericosv/testdata/yaml/GHSA-7fxj-fr3v-r9gj.yaml
@@ -24,5 +24,5 @@
     - web: https://advisory.dw1.io/45
     - web: https://huntr.dev/bounties/120f1346-e958-49d0-b66c-0f889a469540
 notes:
-    - lint: 'github.com/pingcap/tidb: version 6.2.0 does not exist'
-    - lint: 'github.com/pingcap/tidb: version issue: 2 unsupported version(s)'
+    - lint: 'modules[0] "github.com/pingcap/tidb": unsupported_versions: found 2 (want none)'
+    - lint: 'modules[0] "github.com/pingcap/tidb": version 6.2.0 does not exist'
diff --git a/internal/genericosv/testdata/yaml/GHSA-9689-rx4v-cqgc.yaml b/internal/genericosv/testdata/yaml/GHSA-9689-rx4v-cqgc.yaml
index 30edb85..7fd6e28 100644
--- a/internal/genericosv/testdata/yaml/GHSA-9689-rx4v-cqgc.yaml
+++ b/internal/genericosv/testdata/yaml/GHSA-9689-rx4v-cqgc.yaml
@@ -25,5 +25,5 @@
     - web: https://github.com/concourse/concourse/blob/release/5.2.x/release-notes/v5.2.8.md
     - web: https://pivotal.io/security/cve-2018-15798
 notes:
-    - lint: 'github.com/concourse/concourse: 5 versions do not exist: 5.2.8, 5.3.0, 5.5.10, 5.6.0, 5.8.1'
-    - lint: 'github.com/concourse/concourse: missing skip_fix and vulnerable_at: "github.com/concourse/concourse/skymarshal/skyserver"'
+    - lint: 'modules[0] "github.com/concourse/concourse": 5 versions do not exist: 5.2.8, 5.3.0, 5.5.10, 5.6.0, 5.8.1'
+    - lint: 'modules[0] "github.com/concourse/concourse": packages[0] "github.com/concourse/concourse/skymarshal/skyserver": at least one of vulnerable_at and skip_fix must be set'
diff --git a/internal/genericosv/testdata/yaml/GHSA-cf7g-cm7q-rq7f.yaml b/internal/genericosv/testdata/yaml/GHSA-cf7g-cm7q-rq7f.yaml
index 8044f84..fc78087 100644
--- a/internal/genericosv/testdata/yaml/GHSA-cf7g-cm7q-rq7f.yaml
+++ b/internal/genericosv/testdata/yaml/GHSA-cf7g-cm7q-rq7f.yaml
@@ -19,5 +19,5 @@
     - advisory: https://nvd.nist.gov/vuln/detail/CVE-2022-39220
     - fix: https://github.com/drakkan/sftpgo/commit/cbef217cfa92478ee8e00ba1a5fb074f8a8aeee0
 notes:
-    - lint: 'github.com/drakkan/sftpgo: version 2.3.5 does not exist'
-    - lint: references should contain at most one advisory link
+    - lint: 'modules[0] "github.com/drakkan/sftpgo": version 2.3.5 does not exist'
+    - lint: 'references: too many advisories (found 2, want <=1)'
diff --git a/internal/genericosv/testdata/yaml/GHSA-fv82-r8qv-ch4v.yaml b/internal/genericosv/testdata/yaml/GHSA-fv82-r8qv-ch4v.yaml
index 71ee785..884aa26 100644
--- a/internal/genericosv/testdata/yaml/GHSA-fv82-r8qv-ch4v.yaml
+++ b/internal/genericosv/testdata/yaml/GHSA-fv82-r8qv-ch4v.yaml
@@ -36,5 +36,5 @@
     - advisory: https://nvd.nist.gov/vuln/detail/CVE-2021-29652
     - fix: https://github.com/pomerium/pomerium/pull/2048
 notes:
-    - lint: references should contain at most one advisory link
-    - lint: summary should begin with a capital letter
+    - lint: 'references: too many advisories (found 2, want <=1)'
+    - lint: 'summary: must begin with a capital letter'
diff --git a/internal/genericosv/testdata/yaml/GHSA-g5gj-9ggf-9vmq.yaml b/internal/genericosv/testdata/yaml/GHSA-g5gj-9ggf-9vmq.yaml
index 00983be..c09fecb 100644
--- a/internal/genericosv/testdata/yaml/GHSA-g5gj-9ggf-9vmq.yaml
+++ b/internal/genericosv/testdata/yaml/GHSA-g5gj-9ggf-9vmq.yaml
@@ -25,4 +25,4 @@
     - web: https://github.com/cloudflare/cfrpki/releases/tag/v1.4.0
     - web: https://www.debian.org/security/2022/dsa-5041
 notes:
-    - lint: references should contain at most one advisory link
+    - lint: 'references: too many advisories (found 2, want <=1)'
diff --git a/internal/genericosv/testdata/yaml/GHSA-g9wh-3vrx-r7hg.yaml b/internal/genericosv/testdata/yaml/GHSA-g9wh-3vrx-r7hg.yaml
index 2cd6425..01d8c9b 100644
--- a/internal/genericosv/testdata/yaml/GHSA-g9wh-3vrx-r7hg.yaml
+++ b/internal/genericosv/testdata/yaml/GHSA-g9wh-3vrx-r7hg.yaml
@@ -24,4 +24,4 @@
     - fix: https://github.com/cloudflare/cfrpki/commit/648658b1b176a747b52645989cfddc73a81eacad
     - web: https://www.debian.org/security/2022/dsa-5041
 notes:
-    - lint: references should contain at most one advisory link
+    - lint: 'references: too many advisories (found 2, want <=1)'
diff --git a/internal/genericosv/testdata/yaml/GHSA-hjv9-hm2f-rpcj.yaml b/internal/genericosv/testdata/yaml/GHSA-hjv9-hm2f-rpcj.yaml
index c0c3cf0..3e6728c 100644
--- a/internal/genericosv/testdata/yaml/GHSA-hjv9-hm2f-rpcj.yaml
+++ b/internal/genericosv/testdata/yaml/GHSA-hjv9-hm2f-rpcj.yaml
@@ -30,4 +30,4 @@
     - web: https://grafana.com/security/security-advisories/cve-2023-0507/
     - web: https://security.netapp.com/advisory/ntap-20230413-0001/
 notes:
-    - lint: 'github.com/grafana/grafana: 6 versions do not exist: 8.1.0, 8.5.21, 9.0.0, 9.2.13, 9.3.0, 9.3.8'
+    - lint: 'modules[0] "github.com/grafana/grafana": 6 versions do not exist: 8.1.0, 8.5.21, 9.0.0, 9.2.13, 9.3.0, 9.3.8'
diff --git a/internal/genericosv/testdata/yaml/GHSA-hmfx-3pcx-653p.yaml b/internal/genericosv/testdata/yaml/GHSA-hmfx-3pcx-653p.yaml
index 08ab512..134d54f 100644
--- a/internal/genericosv/testdata/yaml/GHSA-hmfx-3pcx-653p.yaml
+++ b/internal/genericosv/testdata/yaml/GHSA-hmfx-3pcx-653p.yaml
@@ -71,4 +71,4 @@
     - web: https://github.com/containerd/containerd/releases/tag/v1.6.18
     - web: https://www.benthamsgaze.org/2022/08/22/vulnerability-in-linux-containers-investigation-and-mitigation/
 notes:
-    - lint: references should contain at most one advisory link
+    - lint: 'references: too many advisories (found 5, want <=1)'
diff --git a/internal/genericosv/testdata/yaml/GHSA-hv53-vf5m-8q94.yaml b/internal/genericosv/testdata/yaml/GHSA-hv53-vf5m-8q94.yaml
index 46a357c..6fbf2aa 100644
--- a/internal/genericosv/testdata/yaml/GHSA-hv53-vf5m-8q94.yaml
+++ b/internal/genericosv/testdata/yaml/GHSA-hv53-vf5m-8q94.yaml
@@ -53,5 +53,5 @@
     - advisory: https://github.com/personnummer/go/security/advisories/GHSA-hv53-vf5m-8q94
     - web: https://pkg.go.dev/github.com/personnummer/go
 notes:
-    - lint: 'github.com/personnummer/go: version 3.0.1 does not exist'
-    - lint: summary should begin with a capital letter
+    - lint: 'modules[0] "github.com/personnummer/go": version 3.0.1 does not exist'
+    - lint: 'summary: must begin with a capital letter'
diff --git a/internal/genericosv/testdata/yaml/GHSA-jmp2-wc4p-wfh2.yaml b/internal/genericosv/testdata/yaml/GHSA-jmp2-wc4p-wfh2.yaml
index 0031d10..595b468 100644
--- a/internal/genericosv/testdata/yaml/GHSA-jmp2-wc4p-wfh2.yaml
+++ b/internal/genericosv/testdata/yaml/GHSA-jmp2-wc4p-wfh2.yaml
@@ -59,5 +59,5 @@
     - web: https://github.com/mutagen-io/mutagen/releases/tag/v0.16.6
     - web: https://github.com/mutagen-io/mutagen/releases/tag/v0.17.1
 notes:
-    - lint: references should contain at most one advisory link
-    - lint: 'summary is too long: 111 characters (max 100)'
+    - lint: 'references: too many advisories (found 2, want <=1)'
+    - lint: 'summary: too long (found 111 characters, want <=100)'
diff --git a/internal/genericosv/testdata/yaml/GHSA-pg5p-wwp8-97g8.yaml b/internal/genericosv/testdata/yaml/GHSA-pg5p-wwp8-97g8.yaml
index bb9d795..13a018a 100644
--- a/internal/genericosv/testdata/yaml/GHSA-pg5p-wwp8-97g8.yaml
+++ b/internal/genericosv/testdata/yaml/GHSA-pg5p-wwp8-97g8.yaml
@@ -61,6 +61,6 @@
     - advisory: https://github.com/cilium/cilium/security/advisories/GHSA-pg5p-wwp8-97g8
     - advisory: https://nvd.nist.gov/vuln/detail/CVE-2023-29002
 notes:
-    - lint: 'github.com/cilium/cilium: version issue: 1 unsupported version(s)'
-    - lint: 'github.com/cilium/cilium: version issue: introduced and fixed versions must alternate'
-    - lint: references should contain at most one advisory link
+    - lint: 'modules[0] "github.com/cilium/cilium": unsupported_versions: found 1 (want none)'
+    - lint: 'modules[0] "github.com/cilium/cilium": versions: introduced and fixed versions must alternate'
+    - lint: 'references: too many advisories (found 2, want <=1)'
diff --git a/internal/genericosv/testdata/yaml/GHSA-pmfr-63c2-jr5c.yaml b/internal/genericosv/testdata/yaml/GHSA-pmfr-63c2-jr5c.yaml
index bc53c54..ab3ef0a 100644
--- a/internal/genericosv/testdata/yaml/GHSA-pmfr-63c2-jr5c.yaml
+++ b/internal/genericosv/testdata/yaml/GHSA-pmfr-63c2-jr5c.yaml
@@ -68,5 +68,5 @@
     - web: http://lists.opensuse.org/opensuse-security-announce/2020-07/msg00059.html
     - web: http://lists.opensuse.org/opensuse-security-announce/2020-09/msg00053.html
 notes:
-    - lint: 'github.com/sylabs/singularity: version 3.6.0 does not exist'
-    - lint: references should contain at most one advisory link
+    - lint: 'modules[0] "github.com/sylabs/singularity": version 3.6.0 does not exist'
+    - lint: 'references: too many advisories (found 2, want <=1)'
diff --git a/internal/genericosv/testdata/yaml/GHSA-w4xh-w33p-4v29.yaml b/internal/genericosv/testdata/yaml/GHSA-w4xh-w33p-4v29.yaml
index a26a4e4..9383164 100644
--- a/internal/genericosv/testdata/yaml/GHSA-w4xh-w33p-4v29.yaml
+++ b/internal/genericosv/testdata/yaml/GHSA-w4xh-w33p-4v29.yaml
@@ -28,6 +28,6 @@
     - web: http://blog.recurity-labs.com/2017-08-10/scm-vulns
     - web: http://www.securityfocus.com/bid/102926
 notes:
-    - lint: 'github.com/git-lfs/git-lfs: missing skip_fix and vulnerable_at: "github.com/git-lfs/git-lfs/lfsapi"'
-    - lint: 'github.com/git-lfs/git-lfs: version 2.1.1-0.20170519163204-f913f5f9c7c6 does not exist'
-    - lint: 'github.com/git-lfs/git-lfs: version 2.1.1-0.20170519163204-f913f5f9c7c6 does not exist'
+    - lint: 'modules[0] "github.com/git-lfs/git-lfs": version 2.1.1-0.20170519163204-f913f5f9c7c6 does not exist'
+    - lint: 'modules[1] "github.com/git-lfs/git-lfs": packages[0] "github.com/git-lfs/git-lfs/lfsapi": at least one of vulnerable_at and skip_fix must be set'
+    - lint: 'modules[1] "github.com/git-lfs/git-lfs": version 2.1.1-0.20170519163204-f913f5f9c7c6 does not exist'
diff --git a/internal/genericosv/testdata/yaml/GHSA-wx8q-rgfr-cf6v.yaml b/internal/genericosv/testdata/yaml/GHSA-wx8q-rgfr-cf6v.yaml
index 4d07ae6..2f05d22 100644
--- a/internal/genericosv/testdata/yaml/GHSA-wx8q-rgfr-cf6v.yaml
+++ b/internal/genericosv/testdata/yaml/GHSA-wx8q-rgfr-cf6v.yaml
@@ -28,5 +28,5 @@
     - advisory: https://nvd.nist.gov/vuln/detail/CVE-2021-22565
     - web: https://github.com/google/exposure-notifications-verification-server/releases/tag/v1.1.2
 notes:
-    - lint: references should contain at most one advisory link
-    - lint: 'summary is too long: 106 characters (max 100)'
+    - lint: 'references: too many advisories (found 2, want <=1)'
+    - lint: 'summary: too long (found 106 characters, want <=100)'
diff --git a/internal/genericosv/testdata/yaml/GHSA-xmg8-99r8-jc2j.yaml b/internal/genericosv/testdata/yaml/GHSA-xmg8-99r8-jc2j.yaml
index 43438d4..ef88a19 100644
--- a/internal/genericosv/testdata/yaml/GHSA-xmg8-99r8-jc2j.yaml
+++ b/internal/genericosv/testdata/yaml/GHSA-xmg8-99r8-jc2j.yaml
@@ -72,5 +72,5 @@
     - web: https://github.com/argoproj/argo-cd/releases/tag/v2.2.9
     - web: https://github.com/argoproj/argo-cd/releases/tag/v2.3.4
 notes:
-    - lint: 'github.com/argoproj/argo-cd: version 2.1.15 does not exist'
-    - lint: references should contain at most one advisory link
+    - lint: 'modules[0] "github.com/argoproj/argo-cd": version 2.1.15 does not exist'
+    - lint: 'references: too many advisories (found 2, want <=1)'
diff --git a/internal/genericosv/testdata/yaml/GHSA-xx9w-464f-7h6f.yaml b/internal/genericosv/testdata/yaml/GHSA-xx9w-464f-7h6f.yaml
index 4cdb56b..60e154f 100644
--- a/internal/genericosv/testdata/yaml/GHSA-xx9w-464f-7h6f.yaml
+++ b/internal/genericosv/testdata/yaml/GHSA-xx9w-464f-7h6f.yaml
@@ -42,4 +42,4 @@
 references:
     - advisory: https://github.com/goharbor/harbor/security/advisories/GHSA-xx9w-464f-7h6f
 notes:
-    - lint: 'github.com/goharbor/harbor: version 1.0.0 does not exist'
+    - lint: 'modules[0] "github.com/goharbor/harbor": version 1.0.0 does not exist'
diff --git a/internal/report/lint.go b/internal/report/lint.go
index 1c698e5..da80704 100644
--- a/internal/report/lint.go
+++ b/internal/report/lint.go
@@ -55,7 +55,7 @@
 		if nf > 0 {
 			sb.WriteString(" and ")
 		}
-		sb.WriteString(fmt.Sprintf("module is not canonical at %d version(s):\n%s", nc, strings.Join(nonCanonical, "\n")))
+		sb.WriteString(fmt.Sprintf("module is not canonical at %d version(s): %s", nc, strings.Join(nonCanonical, ", ")))
 	}
 	if s := sb.String(); s != "" {
 		return errors.New(s)
@@ -63,85 +63,69 @@
 	return nil
 }
 
-func (m *Module) lintStdLib(l *linter) {
-	if len(m.Packages) == 0 {
-		l.Error("missing package")
-	}
-	for _, p := range m.Packages {
-		if p.Package == "" {
-			l.Error("missing package")
-		}
-	}
-}
-
-func (m *Module) lintThirdParty(l *linter) {
-	if m.Module == "" {
-		l.Error("missing module")
-		return
-	}
-	for _, p := range m.Packages {
-		if p.Package == "" {
-			l.Error("missing package")
-			continue
-		}
-		if !strings.HasPrefix(p.Package, m.Module) {
-			l.Error("module must be a prefix of package")
-		}
-		if err := module.CheckImportPath(p.Package); err != nil {
-			l.Error(err)
-		}
-	}
-}
+var missing = "missing"
 
 func (m *Module) lintVersions(l *linter) {
 	if u := len(m.UnsupportedVersions); u > 0 {
-		l.Errorf("version issue: %d unsupported version(s)", u)
+		l.Group("unsupported_versions").Errorf("found %d (want none)", u)
 	}
+
+	vl := l.Group("versions")
 	ranges := AffectedRanges(m.Versions)
 	if v := m.VulnerableAt; v != "" {
 		affected, err := osvutils.AffectsSemver(ranges, v)
 		if err != nil {
-			l.Errorf("version issue: %s", err)
+			vl.Error(err)
 		} else if !affected {
-			l.Errorf("vulnerable_at version %s is not inside vulnerable range", v)
+			l.Group("vulnerable_at").Errorf("%s is not inside vulnerable range", v)
 		}
 	} else {
 		if err := osvutils.ValidateRanges(ranges); err != nil {
-			l.Errorf("version issue: %s", err)
+			vl.Error(err)
 		}
 	}
 }
 
 func (r *Report) lintCVEs(l *linter) {
-	for _, cve := range r.CVEs {
+	for i, cve := range r.CVEs {
 		if !cveschema5.IsCVE(cve) {
-			l.Error("malformed cve identifier")
+			l.Group(name("cves", i, cve)).Error("malformed cve identifier")
 		}
 	}
 }
 
 func (r *Report) lintGHSAs(l *linter) {
-	for _, g := range r.GHSAs {
+	for i, g := range r.GHSAs {
 		if !ghsa.IsGHSA(g) {
-			l.Errorf("%s is not a valid GHSA", g)
+			l.Group(name("ghsas", i, g)).Errorf("%s is not a valid GHSA", g)
 		}
 	}
 }
 
+func name(field string, index int, name string) string {
+	fieldIndex := fmt.Sprintf("%s[%d]", field, index)
+	if name != "" {
+		return fmt.Sprintf("%s %q", fieldIndex, name)
+	}
+	return fieldIndex
+}
+
 func (r *Report) lintRelated(l *linter) {
 	if len(r.Related) == 0 {
+		// Not required.
 		return
 	}
 
 	aliases := r.Aliases()
-	for _, related := range r.Related {
+	for i, related := range r.Related {
+		rl := l.Group(name("related", i, related))
 		// In most cases, the related list is very short, so there's no
 		// need create a map of aliases.
 		if slices.Contains(aliases, related) {
-			l.Errorf("related: identifier %s is also listed among aliases", related)
+			rl.Error("also listed among aliases")
 		}
 		if !isIdentifier(related) {
-			l.Errorf("related: %s is not a recognized identifier (CVE, GHSA or Go ID)", related)
+			rl.Error("not a recognized identifier (CVE, GHSA or Go ID)")
 		}
 	}
 }
@@ -158,7 +142,7 @@
 
 const maxLineLength = 80
 
-func (r *Report) lintLineLength(l *linter, field, content string) {
+func (r *Report) lintLineLength(l *linter, content string) {
 	for _, line := range strings.Split(content, "\n") {
 		if len(line) <= maxLineLength {
 			continue
@@ -166,7 +150,7 @@
 		if !strings.Contains(line, " ") {
 			continue // A single long word is OK.
 		}
-		l.Errorf("%v contains line > %v characters long: %q", field, maxLineLength, line)
+		l.Errorf("contains line > %v characters long: %q", maxLineLength, line)
 		return
 	}
 }
@@ -183,107 +167,110 @@
 	mitreRegex    = regexp.MustCompile(`^https://cve.mitre.org/.*(` + cveschema5.RegexStr + `)$`)
 )
 
-// Checks that the "links" section of a Report for a package in the
-// standard library contains all necessary links, and no third-party links.
-func (r *Report) lintStdLibLinks(l *linter) {
-	var (
-		hasFixLink      = false
-		hasReportLink   = false
-		hasAnnounceLink = false
-	)
-	for _, ref := range r.References {
+func (ref *Reference) lint(l *linter, r *Report) {
+	// Checks specific to first-party reports.
+	if r.IsFirstParty() {
 		switch ref.Type {
 		case osv.ReferenceTypeAdvisory:
-			l.Errorf("%q: advisory reference should not be set for first-party issues", ref.URL)
+			l.Errorf("%q: advisory reference must not be set for first-party issues", ref.URL)
 		case osv.ReferenceTypeFix:
-			hasFixLink = true
 			if !prRegex.MatchString(ref.URL) && !commitRegex.MatchString(ref.URL) {
-				l.Errorf("%q: fix reference should match %q or %q", ref.URL, prRegex, commitRegex)
+				l.Errorf("%q: fix reference must match %q or %q", ref.URL, prRegex, commitRegex)
 			}
 		case osv.ReferenceTypeReport:
-			hasReportLink = true
 			if !issueRegex.MatchString(ref.URL) {
-				l.Errorf("%q: report reference should match %q", ref.URL, issueRegex)
+				l.Errorf("%q: report reference must match regex %q", ref.URL, issueRegex)
 			}
 		case osv.ReferenceTypeWeb:
 			if !announceRegex.MatchString(ref.URL) {
-				l.Errorf("%q: web references should only contain announcement links matching %q", ref.URL, announceRegex)
-			} else {
-				hasAnnounceLink = true
+				l.Errorf("%q: web reference must match regex %q", ref.URL, announceRegex)
 			}
 		}
 	}
-	if !hasFixLink {
-		l.Error("references should contain at least one fix")
+
+	if !slices.Contains(osv.ReferenceTypes, ref.Type) {
+		l.Errorf("invalid reference type %q", ref.Type)
 	}
-	if !hasReportLink {
-		l.Error("references should contain at least one report")
+	u := ref.URL
+	if _, err := url.ParseRequestURI(u); err != nil {
+		l.Error("invalid URL")
 	}
-	if !hasAnnounceLink {
-		l.Errorf("references should contain an announcement link matching %q", announceRegex)
+	if fixed := fixURL(u); fixed != u {
+		l.Errorf("should be %q (can be auto-fixed)", fixURL(u))
+	}
+	if ref.Type != osv.ReferenceTypeAdvisory {
+		// An ADVISORY reference to a CVE/GHSA indicates that it
+		// is the canonical source of information on this vuln.
+		//
+		// A reference to a CVE/GHSA that is not an alias of this
+		// report indicates that it may contain related information.
+		//
+		// A reference to a CVE/GHSA that appears in the CVEs/GHSAs
+		// aliases is redundant.
+		for _, re := range []*regexp.Regexp{nistRegex, mitreRegex, ghsaLinkRegex} {
+			if m := re.FindStringSubmatch(ref.URL); len(m) > 0 {
+				id := m[1]
+				if slices.Contains(r.CVEs, id) || slices.Contains(r.GHSAs, id) {
+					l.Errorf("redundant non-advisory reference to %v", id)
+				}
+			}
+		}
 	}
 }
 
 func (r *Report) lintReferences(l *linter) {
 	advisoryCount := 0
-	for _, ref := range r.References {
-		if !slices.Contains(osv.ReferenceTypes, ref.Type) {
-			l.Errorf("%q is not a valid reference type", ref.Type)
-		}
-		u := ref.URL
-		if _, err := url.ParseRequestURI(u); err != nil {
-			l.Errorf("%q is not a valid URL", u)
-		}
-		if fixed := fixURL(u); fixed != u {
-			l.Errorf("unfixed url: %q should be %q", u, fixURL(u))
-		}
+	for i, ref := range r.References {
+		rl := l.Group(name("references", i, ref.URL))
+		ref.lint(rl, r)
 		if ref.Type == osv.ReferenceTypeAdvisory {
 			advisoryCount++
 		}
-		if ref.Type != osv.ReferenceTypeAdvisory {
-			// An ADVISORY reference to a CVE/GHSA indicates that it
-			// is the canonical source of information on this vuln.
-			//
-			// A reference to a CVE/GHSA that is not an alias of this
-			// report indicates that it may contain related information.
-			//
-			// A reference to a CVE/GHSA that appears in the CVEs/GHSAs
-			// aliases is redundant.
-			for _, re := range []*regexp.Regexp{nistRegex, mitreRegex, ghsaLinkRegex} {
-				if m := re.FindStringSubmatch(ref.URL); len(m) > 0 {
-					id := m[1]
-					if slices.Contains(r.CVEs, id) || slices.Contains(r.GHSAs, id) {
-						l.Errorf("redundant non-advisory reference to %v", id)
+	}
+
+	// Find missing references.
+	rl := l.Group("references")
+	if advisoryCount > 1 {
+		rl.Errorf("too many advisories (found %d, want <=1)", advisoryCount)
+	}
+	if !r.IsExcluded() {
+		if r.IsFirstParty() {
+			var hasFixLink, hasReportLink, hasAnnounceLink bool
+			for _, ref := range r.References {
+				switch ref.Type {
+				case osv.ReferenceTypeFix:
+					hasFixLink = true
+				case osv.ReferenceTypeReport:
+					hasReportLink = true
+				case osv.ReferenceTypeWeb:
+					if announceRegex.MatchString(ref.URL) {
+						hasAnnounceLink = true
 					}
 				}
 			}
+			if !hasFixLink {
+				rl.Error("must contain at least one fix")
+			}
+			if !hasReportLink {
+				rl.Error("must contain at least one report")
+			}
+			if !hasAnnounceLink {
+				rl.Errorf("must contain an announcement link matching regex %q", announceRegex)
+			}
 		}
-	}
-	if advisoryCount > 1 {
-		l.Error("references should contain at most one advisory link")
-	}
-	if r.IsFirstParty() && !r.IsExcluded() {
-		r.lintStdLibLinks(l)
+		if advisoryCount == 0 && r.Description == "" && r.CVEMetadata == nil {
+			rl.Error("missing advisory (required because report has no description)")
+		}
 	}
 }
 
 func (d *Description) lint(l *linter, r *Report) {
 	desc := d.String()
 
-	r.lintLineLength(l, "description", desc)
-	hasAdvisory := func() bool {
-		for _, ref := range r.References {
-			if ref.Type == osv.ReferenceTypeAdvisory {
-				return true
-			}
-		}
-		return false
-	}
+	r.lintLineLength(l, desc)
 	if !r.IsExcluded() && desc == "" {
 		if r.CVEMetadata != nil {
-			l.Error("missing description (reports with Go CVEs must have a description)")
-		} else if !hasAdvisory() {
-			l.Error("missing advisory (reports without descriptions must have an advisory link)")
+			l.Error("missing (reports with Go CVEs must have a description)")
 		}
 	}
 }
@@ -291,27 +278,27 @@
 func (s *Summary) lint(l *linter, r *Report) {
 	summary := s.String()
 	if !r.IsExcluded() && len(summary) == 0 {
-		l.Error("missing summary")
+		l.Error(missing)
 	}
 	// Nothing to lint.
 	if len(summary) == 0 {
 		return
 	}
-	if strings.HasPrefix(summary, "TODO") {
-		l.Error("summary contains a TODO")
+	if hasTODO(summary) {
+		l.Error(hasTODOErr)
 	}
 	if ln := len(summary); ln > 100 {
-		l.Errorf("summary is too long: %d characters (max 100)", ln)
+		l.Errorf("too long (found %d characters, want <=100)", ln)
 	}
 	if strings.HasSuffix(summary, ".") {
-		l.Error("summary should not end in a period (should be a phrase, not a sentence)")
+		l.Error("must not end in a period (should be a phrase, not a sentence)")
 	}
 	for i, r := range summary {
 		if i != 0 {
 			break
 		}
 		if !unicode.IsUpper(r) {
-			l.Error("summary should begin with a capital letter")
+			l.Error("must begin with a capital letter")
 		}
 	}
 }
@@ -392,18 +379,19 @@
 	l := NewLinter("")
 
 	if r.ID == "" {
-		l.Error("missing ID")
+		l.Group("id").Error(missing)
 	}
-	r.Summary.lint(l, r)
-	r.Description.lint(l, r)
-	r.Excluded.lint(l)
+
+	r.Summary.lint(l.Group("summary"), r)
+	r.Description.lint(l.Group("description"), r)
+	r.Excluded.lint(l.Group("excluded"))
 
 	r.lintModules(l, pc)
 
-	r.CVEMetadata.lint(l, r)
+	r.CVEMetadata.lint(l.Group("cve_metadata"), r)
 
 	if r.IsExcluded() && len(r.Aliases()) == 0 {
-		l.Error("excluded report must have at least one associated CVE or GHSA")
+		l.Group("cves,ghsas").Error()
 	}
 
 	r.lintCVEs(l)
@@ -416,48 +404,62 @@
 }
 
 func (m *Module) lint(l *linter, r *Report, pc *proxy.Client) {
-	if m.IsFirstParty() {
-		m.lintStdLib(l)
-	} else {
-		m.lintThirdParty(l)
-		if pc != nil {
-			if err := m.checkModVersions(pc); err != nil {
-				l.Error(err.Error())
-			}
+	if m.Module == "" {
+		l.Error("no module name")
+	}
+
+	if !m.IsFirstParty() && pc != nil {
+		if err := m.checkModVersions(pc); err != nil {
+			l.Error(err)
 		}
 	}
 
-	for _, p := range m.Packages {
-		p.lint(l, m, r)
+	if m.IsFirstParty() && len(m.Packages) == 0 {
+		l.Error("no packages")
+	}
+
+	for i, p := range m.Packages {
+		p.lint(l.Group(name("packages", i, p.Package)), m, r)
 	}
 
 	m.lintVersions(l)
 }
 
 func (p *Package) lint(l *linter, m *Module, r *Report) {
-	if strings.HasPrefix(p.Package, fmt.Sprintf("%s/", stdlib.ToolchainModulePath)) &&
-		m.Module != stdlib.ToolchainModulePath {
-		l.Errorf(`%q should be in module "%s", not %q`, p.Package, stdlib.ToolchainModulePath, m.Module)
+	if p.Package == "" {
+		l.Error("no package name")
+	} else {
+		if m.Module != stdlib.ModulePath {
+			if !strings.HasPrefix(p.Package, m.Module) {
+				l.Error("module must be a prefix of package")
+			}
+		} else if strings.HasPrefix(p.Package, stdlib.ToolchainModulePath) {
+			// As a special case, check for "cmd/" packages that are
+			// mistakenly placed in the "std" module.
+			l.Error("must be in module cmd")
+		}
+
+		if !m.IsFirstParty() {
+			if err := module.CheckImportPath(p.Package); err != nil {
+				l.Error(err)
+			}
+		}
 	}
 
 	if !r.IsExcluded() {
 		if m.VulnerableAt == "" && p.SkipFix == "" {
-			l.Errorf("missing skip_fix and vulnerable_at: %q", p.Package)
+			l.Error("at least one of vulnerable_at and skip_fix must be set")
 		}
 	}
 }
 
 func (r *Report) lintModules(l *linter, pc *proxy.Client) {
 	if r.Excluded != "NOT_GO_CODE" && len(r.Modules) == 0 {
-		l.Error("no modules")
+		l.Group("modules").Error(missing)
 	}
 
 	for i, m := range r.Modules {
-		mod := m.Module
-		if mod == "" {
-			mod = fmt.Sprintf("modules[%d]", i)
-		}
-		m.lint(l.Group(mod), r, pc)
+		m.lint(l.Group(name("modules", i, m.Module)), r, pc)
 	}
 }
 
@@ -488,16 +490,26 @@
 		return
 	}
 
+	il := l.Group("id")
 	if m.ID == "" {
-		l.Error("cve_metadata.id is required")
+		il.Error(missing)
 	} else if !cveschema5.IsCVE(m.ID) {
-		l.Error("malformed cve_metadata.id identifier")
+		il.Error("not a valid CVE")
 	}
+
+	cl := l.Group("cwe")
 	if m.CWE == "" {
-		l.Error("cve_metadata.cwe is required")
+		cl.Error(missing)
 	}
-	if strings.Contains(m.CWE, "TODO") {
-		l.Error("cve_metadata.cwe contains a TODO")
+	if hasTODO(m.CWE) {
+		cl.Error(hasTODOErr)
 	}
-	r.lintLineLength(l, "cve_metadata.cwe", m.Description)
+
+	r.lintLineLength(l.Group("description"), m.Description)
+}
+
+var hasTODOErr = "contains a TODO"
+
+func hasTODO(s string) bool {
+	return strings.Contains(s, "TODO")
 }
diff --git a/internal/report/lint_test.go b/internal/report/lint_test.go
index c0e16ab..8c0422d 100644
--- a/internal/report/lint_test.go
+++ b/internal/report/lint_test.go
@@ -15,6 +15,7 @@
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
 	"golang.org/x/tools/txtar"
 	"golang.org/x/vulndb/internal/osv"
 	"golang.org/x/vulndb/internal/proxy"
@@ -831,9 +832,19 @@
 
 	want, got := []*Note{
 		{Body: "a note added by a human", Type: NoteTypeNone}, // preserved
-		{Body: "missing summary", Type: NoteTypeLint},
+		{Body: "summary: missing", Type: NoteTypeLint},
 		{Body: "proxy client is nil; cannot perform all lint checks", Type: NoteTypeLint}}, report.Notes
-	if diff := cmp.Diff(want, got); diff != "" {
+	if diff := cmp.Diff(want, got,
+		// ignore order
+		cmpopts.SortSlices(
+			func(a, b *Note) bool {
+				if a.Type < b.Type {
+					return true
+				} else if a.Type > b.Type {
+					return false
+				}
+				return a.Body < b.Body
+			})); diff != "" {
 		t.Errorf("mismatch (-want, +got):\n%s", diff)
 	}
 }
diff --git a/internal/report/testdata/lint/TestLint/module_non_canonical.txtar b/internal/report/testdata/lint/TestLint/module_non_canonical.txtar
index 1cb665f..78b6996 100644
--- a/internal/report/testdata/lint/TestLint/module_non_canonical.txtar
+++ b/internal/report/testdata/lint/TestLint/module_non_canonical.txtar
@@ -21,5 +21,4 @@
     - CVE-1234-0000
 
 -- golden --
-github.com/golang/vuln: module is not canonical at 1 version(s):
-0.1.0 (canonical:golang.org/x/vuln)
+modules[1] "github.com/golang/vuln": module is not canonical at 1 version(s): 0.1.0 (canonical:golang.org/x/vuln)
diff --git a/internal/report/testdata/lint/TestLint/module_version_invalid.txtar b/internal/report/testdata/lint/TestLint/module_version_invalid.txtar
index 3055fca..ad647e8 100644
--- a/internal/report/testdata/lint/TestLint/module_version_invalid.txtar
+++ b/internal/report/testdata/lint/TestLint/module_version_invalid.txtar
@@ -21,4 +21,4 @@
     - CVE-1234-0000
 
 -- golden --
-golang.org/x/net: version 0.2.5 does not exist
+modules[1] "golang.org/x/net": version 0.2.5 does not exist
diff --git a/internal/report/testdata/lint/TestLint/multiple_problems.txtar b/internal/report/testdata/lint/TestLint/multiple_problems.txtar
index ca6bcc7..337bb44 100644
--- a/internal/report/testdata/lint/TestLint/multiple_problems.txtar
+++ b/internal/report/testdata/lint/TestLint/multiple_problems.txtar
@@ -23,5 +23,4 @@
     - CVE-1234-0000
 
 -- golden --
-github.com/golang/vuln: 2 versions do not exist: 0.2.5, 0.2.6 and module is not canonical at 1 version(s):
-0.1.0 (canonical:golang.org/x/vuln)
+modules[1] "github.com/golang/vuln": 2 versions do not exist: 0.2.5, 0.2.6 and module is not canonical at 1 version(s): 0.1.0 (canonical:golang.org/x/vuln)
diff --git a/internal/report/testdata/lint/TestLintOffline/bad_cve.txtar b/internal/report/testdata/lint/TestLintOffline/bad_cve.txtar
index 1eae227..2e134be 100644
--- a/internal/report/testdata/lint/TestLintOffline/bad_cve.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/bad_cve.txtar
@@ -18,4 +18,4 @@
     - CVE.1234.5678
 
 -- golden --
-malformed cve identifier
+cves[0] "CVE.1234.5678": malformed cve identifier
diff --git a/internal/report/testdata/lint/TestLintOffline/bad_ghsa.txtar b/internal/report/testdata/lint/TestLintOffline/bad_ghsa.txtar
index 4f4f580..b2d1a43 100644
--- a/internal/report/testdata/lint/TestLintOffline/bad_ghsa.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/bad_ghsa.txtar
@@ -20,4 +20,4 @@
     - GHSA-123
 
 -- golden --
-GHSA-123 is not a valid GHSA
+ghsas[0] "GHSA-123": GHSA-123 is not a valid GHSA
diff --git a/internal/report/testdata/lint/TestLintOffline/bad_related.txtar b/internal/report/testdata/lint/TestLintOffline/bad_related.txtar
index 5edc557..1c7ab36 100644
--- a/internal/report/testdata/lint/TestLintOffline/bad_related.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/bad_related.txtar
@@ -24,5 +24,5 @@
     - GO-1990-0001
 
 -- golden --
-related: not-an-id is not a recognized identifier (CVE, GHSA or Go ID)
-related: identifier CVE-0000-1111 is also listed among aliases
+related[0] "not-an-id": not a recognized identifier (CVE, GHSA or Go ID)
+related[1] "CVE-0000-1111": also listed among aliases
diff --git a/internal/report/testdata/lint/TestLintOffline/cve_metadata_bad_fields.txtar b/internal/report/testdata/lint/TestLintOffline/cve_metadata_bad_fields.txtar
index 55dff46..4d75954 100644
--- a/internal/report/testdata/lint/TestLintOffline/cve_metadata_bad_fields.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/cve_metadata_bad_fields.txtar
@@ -19,5 +19,5 @@
     cwe: TODO
 
 -- golden --
-malformed cve_metadata.id identifier
-cve_metadata.cwe contains a TODO
+cve_metadata: id: not a valid CVE
+cve_metadata: cwe: contains a TODO
diff --git a/internal/report/testdata/lint/TestLintOffline/cve_metadata_missing_fields.txtar b/internal/report/testdata/lint/TestLintOffline/cve_metadata_missing_fields.txtar
index 1b2cdd0..5b705f3 100644
--- a/internal/report/testdata/lint/TestLintOffline/cve_metadata_missing_fields.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/cve_metadata_missing_fields.txtar
@@ -17,5 +17,5 @@
 cve_metadata: {}
 
 -- golden --
-cve_metadata.id is required
-cve_metadata.cwe is required
+cve_metadata: id: missing
+cve_metadata: cwe: missing
diff --git a/internal/report/testdata/lint/TestLintOffline/description_line_length.txtar b/internal/report/testdata/lint/TestLintOffline/description_line_length.txtar
index 0e01e69..0a29d8f 100644
--- a/internal/report/testdata/lint/TestLintOffline/description_line_length.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/description_line_length.txtar
@@ -18,4 +18,4 @@
     - CVE-1234-0000
 
 -- golden --
-description contains line > 80 characters long: "This line is too long; it needs to be shortened to less than 80 characters to pass the lint check"
+description: contains line > 80 characters long: "This line is too long; it needs to be shortened to less than 80 characters to pass the lint check"
diff --git a/internal/report/testdata/lint/TestLintOffline/invalid_package_path.txtar b/internal/report/testdata/lint/TestLintOffline/invalid_package_path.txtar
index f782881..220a91c 100644
--- a/internal/report/testdata/lint/TestLintOffline/invalid_package_path.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/invalid_package_path.txtar
@@ -18,4 +18,4 @@
     - CVE-1234-0000
 
 -- golden --
-invalid.: malformed import path "invalid.": trailing dot in path element
+modules[0] "invalid.": packages[0] "invalid.": malformed import path "invalid.": trailing dot in path element
diff --git a/internal/report/testdata/lint/TestLintOffline/invalid_semver.txtar b/internal/report/testdata/lint/TestLintOffline/invalid_semver.txtar
index 988bd47..e352f7e 100644
--- a/internal/report/testdata/lint/TestLintOffline/invalid_semver.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/invalid_semver.txtar
@@ -22,4 +22,4 @@
     - report: https://go.dev/issue/12345
 
 -- golden --
-std: version issue: invalid or non-canonical semver version (found 1.3.X)
+modules[0] "std": versions: invalid or non-canonical semver version (found 1.3.X)
diff --git a/internal/report/testdata/lint/TestLintOffline/missing_fields_excluded.txtar b/internal/report/testdata/lint/TestLintOffline/missing_fields_excluded.txtar
index ed9346b..74bac3e 100644
--- a/internal/report/testdata/lint/TestLintOffline/missing_fields_excluded.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/missing_fields_excluded.txtar
@@ -10,6 +10,6 @@
 excluded: not a real reason
 
 -- golden --
-excluded reason ("not a real reason") is not a valid excluded reason (accepted: [NOT_IMPORTABLE NOT_GO_CODE NOT_A_VULNERABILITY EFFECTIVELY_PRIVATE DEPENDENT_VULNERABILITY LEGACY_FALSE_POSITIVE])
-no modules
-excluded report must have at least one associated CVE or GHSA
+excluded: excluded reason ("not a real reason") is not a valid excluded reason (accepted: [NOT_IMPORTABLE NOT_GO_CODE NOT_A_VULNERABILITY EFFECTIVELY_PRIVATE DEPENDENT_VULNERABILITY LEGACY_FALSE_POSITIVE])
+modules: missing
+cves,ghsas: 
diff --git a/internal/report/testdata/lint/TestLintOffline/module_package_prefix.txtar b/internal/report/testdata/lint/TestLintOffline/module_package_prefix.txtar
index dfce919..d5c69cc 100644
--- a/internal/report/testdata/lint/TestLintOffline/module_package_prefix.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/module_package_prefix.txtar
@@ -18,4 +18,4 @@
     - CVE-1234-0000
 
 -- golden --
-example.com/module: module must be a prefix of package
+modules[0] "example.com/module": packages[0] "example.com/package": module must be a prefix of package
diff --git a/internal/report/testdata/lint/TestLintOffline/no_ID.txtar b/internal/report/testdata/lint/TestLintOffline/no_ID.txtar
index 68b8a84..7d97c47 100644
--- a/internal/report/testdata/lint/TestLintOffline/no_ID.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/no_ID.txtar
@@ -17,4 +17,4 @@
     - CVE-1234-0000
 
 -- golden --
-missing ID
+id: missing
diff --git a/internal/report/testdata/lint/TestLintOffline/no_advisory.txtar b/internal/report/testdata/lint/TestLintOffline/no_advisory.txtar
index 047286a..94f9832 100644
--- a/internal/report/testdata/lint/TestLintOffline/no_advisory.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/no_advisory.txtar
@@ -17,4 +17,4 @@
     - CVE-1234-0000
 
 -- golden --
-missing advisory (reports without descriptions must have an advisory link)
+references: missing advisory (required because report has no description)
diff --git a/internal/report/testdata/lint/TestLintOffline/no_description_go_cve.txtar b/internal/report/testdata/lint/TestLintOffline/no_description_go_cve.txtar
index 8a0a3f7..a1d3ac3 100644
--- a/internal/report/testdata/lint/TestLintOffline/no_description_go_cve.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/no_description_go_cve.txtar
@@ -18,4 +18,4 @@
     cwe: 'CWE XXX: A CWE description'
 
 -- golden --
-missing description (reports with Go CVEs must have a description)
+description: missing (reports with Go CVEs must have a description)
diff --git a/internal/report/testdata/lint/TestLintOffline/no_module_path.txtar b/internal/report/testdata/lint/TestLintOffline/no_module_path.txtar
index 17f9c24..226df20 100644
--- a/internal/report/testdata/lint/TestLintOffline/no_module_path.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/no_module_path.txtar
@@ -17,4 +17,4 @@
     - CVE-1234-0000
 
 -- golden --
-modules[0]: missing module
+modules[0]: no module name
diff --git a/internal/report/testdata/lint/TestLintOffline/no_modules.txtar b/internal/report/testdata/lint/TestLintOffline/no_modules.txtar
index 9d240cc..8f82c77 100644
--- a/internal/report/testdata/lint/TestLintOffline/no_modules.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/no_modules.txtar
@@ -13,4 +13,4 @@
     - CVE-1234-0000
 
 -- golden --
-no modules
+modules: missing
diff --git a/internal/report/testdata/lint/TestLintOffline/no_package_path.txtar b/internal/report/testdata/lint/TestLintOffline/no_package_path.txtar
index debae1c..800cca0 100644
--- a/internal/report/testdata/lint/TestLintOffline/no_package_path.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/no_package_path.txtar
@@ -18,4 +18,4 @@
     - CVE-1234-0000
 
 -- golden --
-golang.org/x/net: missing package
+modules[0] "golang.org/x/net": packages[0]: no package name
diff --git a/internal/report/testdata/lint/TestLintOffline/no_package_path_stdlib.txtar b/internal/report/testdata/lint/TestLintOffline/no_package_path_stdlib.txtar
index d2ea0f8..b01972f 100644
--- a/internal/report/testdata/lint/TestLintOffline/no_package_path_stdlib.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/no_package_path_stdlib.txtar
@@ -20,4 +20,4 @@
     - report: https://go.dev/issue/12345
 
 -- golden --
-std: missing package
+modules[0] "std": packages[0]: no package name
diff --git a/internal/report/testdata/lint/TestLintOffline/no_package_stdlib.txtar b/internal/report/testdata/lint/TestLintOffline/no_package_stdlib.txtar
index ed37328..1411e40 100644
--- a/internal/report/testdata/lint/TestLintOffline/no_package_stdlib.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/no_package_stdlib.txtar
@@ -18,4 +18,4 @@
     - report: https://go.dev/issue/12345
 
 -- golden --
-std: missing package
+modules[0] "std": no packages
diff --git a/internal/report/testdata/lint/TestLintOffline/no_summary.txtar b/internal/report/testdata/lint/TestLintOffline/no_summary.txtar
index 1383d43..ff75fdc 100644
--- a/internal/report/testdata/lint/TestLintOffline/no_summary.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/no_summary.txtar
@@ -17,4 +17,4 @@
     - CVE-1234-0000
 
 -- golden --
-missing summary
+summary: missing
diff --git a/internal/report/testdata/lint/TestLintOffline/no_vulnerable_at_or_skip_fix.txtar b/internal/report/testdata/lint/TestLintOffline/no_vulnerable_at_or_skip_fix.txtar
index 2249403..443d931 100644
--- a/internal/report/testdata/lint/TestLintOffline/no_vulnerable_at_or_skip_fix.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/no_vulnerable_at_or_skip_fix.txtar
@@ -17,4 +17,4 @@
     - CVE-1234-0000
 
 -- golden --
-golang.org/x/net: missing skip_fix and vulnerable_at: "golang.org/x/net/http2"
+modules[0] "golang.org/x/net": packages[0] "golang.org/x/net/http2": at least one of vulnerable_at and skip_fix must be set
diff --git a/internal/report/testdata/lint/TestLintOffline/reference_invalid_URL.txtar b/internal/report/testdata/lint/TestLintOffline/reference_invalid_URL.txtar
index 56aa0a3..29c0c82 100644
--- a/internal/report/testdata/lint/TestLintOffline/reference_invalid_URL.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/reference_invalid_URL.txtar
@@ -20,4 +20,4 @@
     - fix: go.dev/cl/12345
 
 -- golden --
-"go.dev/cl/12345" is not a valid URL
+references[0] "go.dev/cl/12345": invalid URL
diff --git a/internal/report/testdata/lint/TestLintOffline/reference_invalid_type.txtar b/internal/report/testdata/lint/TestLintOffline/reference_invalid_type.txtar
index eccc90f..7f77cb2 100644
--- a/internal/report/testdata/lint/TestLintOffline/reference_invalid_type.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/reference_invalid_type.txtar
@@ -20,4 +20,4 @@
     - invalid: http://go.dev/
 
 -- golden --
-"INVALID" is not a valid reference type
+references[0] "http://go.dev/": invalid reference type "INVALID"
diff --git a/internal/report/testdata/lint/TestLintOffline/references_incorrect_stdlib.txtar b/internal/report/testdata/lint/TestLintOffline/references_incorrect_stdlib.txtar
index 944aaf9..83c20b8 100644
--- a/internal/report/testdata/lint/TestLintOffline/references_incorrect_stdlib.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/references_incorrect_stdlib.txtar
@@ -22,11 +22,11 @@
     - web: https://go.dev/
 
 -- golden --
-unfixed url: "https://github.com/golang/go/commit/12345" should be "https://go.googlesource.com/+/12345"
-unfixed url: "https://github.com/golang/go/issues/12345" should be "https://go.dev/issue/12345"
-"http://www.example.com": advisory reference should not be set for first-party issues
-"https://go-review.googlesource.com/c/go/+/12345": fix reference should match "https://go.dev/cl/\\d+" or "https://go.googlesource.com/[^/]+/\\+/([^/]+)"
-"https://github.com/golang/go/commit/12345": fix reference should match "https://go.dev/cl/\\d+" or "https://go.googlesource.com/[^/]+/\\+/([^/]+)"
-"https://github.com/golang/go/issues/12345": report reference should match "https://go.dev/issue/\\d+"
-"https://go.dev/": web references should only contain announcement links matching "https://groups.google.com/g/golang-(announce|dev|nuts)/c/([^/]+)"
-references should contain an announcement link matching "https://groups.google.com/g/golang-(announce|dev|nuts)/c/([^/]+)"
+references[0] "http://www.example.com": "http://www.example.com": advisory reference must not be set for first-party issues
+references[1] "https://go-review.googlesource.com/c/go/+/12345": "https://go-review.googlesource.com/c/go/+/12345": fix reference must match "https://go.dev/cl/\\d+" or "https://go.googlesource.com/[^/]+/\\+/([^/]+)"
+references[2] "https://github.com/golang/go/commit/12345": "https://github.com/golang/go/commit/12345": fix reference must match "https://go.dev/cl/\\d+" or "https://go.googlesource.com/[^/]+/\\+/([^/]+)"
+references[2] "https://github.com/golang/go/commit/12345": should be "https://go.googlesource.com/+/12345" (can be auto-fixed)
+references[3] "https://github.com/golang/go/issues/12345": "https://github.com/golang/go/issues/12345": report reference must match regex "https://go.dev/issue/\\d+"
+references[3] "https://github.com/golang/go/issues/12345": should be "https://go.dev/issue/12345" (can be auto-fixed)
+references[4] "https://go.dev/": "https://go.dev/": web reference must match regex "https://groups.google.com/g/golang-(announce|dev|nuts)/c/([^/]+)"
+references: must contain an announcement link matching regex "https://groups.google.com/g/golang-(announce|dev|nuts)/c/([^/]+)"
diff --git a/internal/report/testdata/lint/TestLintOffline/references_missing_stdlib.txtar b/internal/report/testdata/lint/TestLintOffline/references_missing_stdlib.txtar
index a0b38bf..611ea4f 100644
--- a/internal/report/testdata/lint/TestLintOffline/references_missing_stdlib.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/references_missing_stdlib.txtar
@@ -16,6 +16,6 @@
 description: description
 
 -- golden --
-references should contain at least one fix
-references should contain at least one report
-references should contain an announcement link matching "https://groups.google.com/g/golang-(announce|dev|nuts)/c/([^/]+)"
+references: must contain at least one fix
+references: must contain at least one report
+references: must contain an announcement link matching regex "https://groups.google.com/g/golang-(announce|dev|nuts)/c/([^/]+)"
diff --git a/internal/report/testdata/lint/TestLintOffline/references_multiple_advisories.txtar b/internal/report/testdata/lint/TestLintOffline/references_multiple_advisories.txtar
index eb59a32..57d4182 100644
--- a/internal/report/testdata/lint/TestLintOffline/references_multiple_advisories.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/references_multiple_advisories.txtar
@@ -21,4 +21,4 @@
     - advisory: http://go.dev/b
 
 -- golden --
-references should contain at most one advisory link
+references: too many advisories (found 2, want <=1)
diff --git a/internal/report/testdata/lint/TestLintOffline/references_redundant_web_advisories.txtar b/internal/report/testdata/lint/TestLintOffline/references_redundant_web_advisories.txtar
index 41c9b94..84c6c9c 100644
--- a/internal/report/testdata/lint/TestLintOffline/references_redundant_web_advisories.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/references_redundant_web_advisories.txtar
@@ -27,6 +27,6 @@
     - web: https://github.com/advisories/GHSA-0000-0000-0001
 
 -- golden --
-redundant non-advisory reference to CVE-0000-0000
-redundant non-advisory reference to CVE-0000-0001
-redundant non-advisory reference to GHSA-0000-0000-0000
+references[0] "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-0000-0000": redundant non-advisory reference to CVE-0000-0000
+references[1] "https://nvd.nist.gov/vuln/detail/CVE-0000-0001": redundant non-advisory reference to CVE-0000-0001
+references[3] "https://github.com/advisories/GHSA-0000-0000-0000": redundant non-advisory reference to GHSA-0000-0000-0000
diff --git a/internal/report/testdata/lint/TestLintOffline/references_unfixed.txtar b/internal/report/testdata/lint/TestLintOffline/references_unfixed.txtar
index 7c2d16f..6ed2673 100644
--- a/internal/report/testdata/lint/TestLintOffline/references_unfixed.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/references_unfixed.txtar
@@ -23,7 +23,7 @@
     - web: https://groups.google.com/forum/#!/golang-announce/12345/1/
 
 -- golden --
-unfixed url: "https://github.com/golang/go/commit/12345" should be "https://go.googlesource.com/+/12345"
-unfixed url: "https://github.com/golang/go/issues/12345" should be "https://go.dev/issue/12345"
-unfixed url: "https://golang.org/xxx" should be "https://go.dev/xxx"
-unfixed url: "https://groups.google.com/forum/#!/golang-announce/12345/1/" should be "https://groups.google.com/g/golang-announce/c/12345/m/1/"
+references[0] "https://github.com/golang/go/commit/12345": should be "https://go.googlesource.com/+/12345" (can be auto-fixed)
+references[1] "https://github.com/golang/go/issues/12345": should be "https://go.dev/issue/12345" (can be auto-fixed)
+references[2] "https://golang.org/xxx": should be "https://go.dev/xxx" (can be auto-fixed)
+references[3] "https://groups.google.com/forum/#!/golang-announce/12345/1/": should be "https://groups.google.com/g/golang-announce/c/12345/m/1/" (can be auto-fixed)
diff --git a/internal/report/testdata/lint/TestLintOffline/summary_period.txtar b/internal/report/testdata/lint/TestLintOffline/summary_period.txtar
index 81036c9..e40df93 100644
--- a/internal/report/testdata/lint/TestLintOffline/summary_period.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/summary_period.txtar
@@ -18,4 +18,4 @@
     - CVE-1234-0000
 
 -- golden --
-summary should not end in a period (should be a phrase, not a sentence)
+summary: must not end in a period (should be a phrase, not a sentence)
diff --git a/internal/report/testdata/lint/TestLintOffline/summary_todo.txtar b/internal/report/testdata/lint/TestLintOffline/summary_todo.txtar
index afeae0a..f6f5fc5 100644
--- a/internal/report/testdata/lint/TestLintOffline/summary_todo.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/summary_todo.txtar
@@ -18,4 +18,4 @@
     - CVE-1234-0000
 
 -- golden --
-summary contains a TODO
+summary: contains a TODO
diff --git a/internal/report/testdata/lint/TestLintOffline/summary_too_long.txtar b/internal/report/testdata/lint/TestLintOffline/summary_too_long.txtar
index 3ea3841..8e189d9 100644
--- a/internal/report/testdata/lint/TestLintOffline/summary_too_long.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/summary_too_long.txtar
@@ -18,4 +18,4 @@
     - CVE-1234-0000
 
 -- golden --
-summary is too long: 101 characters (max 100)
+summary: too long (found 101 characters, want <=100)
diff --git a/internal/report/testdata/lint/TestLintOffline/unsupported_versions.txtar b/internal/report/testdata/lint/TestLintOffline/unsupported_versions.txtar
index 836b80c..e69a3ad 100644
--- a/internal/report/testdata/lint/TestLintOffline/unsupported_versions.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/unsupported_versions.txtar
@@ -23,4 +23,4 @@
     - report: https://go.dev/issue/12345
 
 -- golden --
-std: version issue: 1 unsupported version(s)
+modules[0] "std": unsupported_versions: found 1 (want none)
diff --git a/internal/report/testdata/lint/TestLintOffline/versions_checked_no_vulnerable_at.txtar b/internal/report/testdata/lint/TestLintOffline/versions_checked_no_vulnerable_at.txtar
index 6de0081..a79a0b4 100644
--- a/internal/report/testdata/lint/TestLintOffline/versions_checked_no_vulnerable_at.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/versions_checked_no_vulnerable_at.txtar
@@ -22,5 +22,5 @@
     - report: https://go.dev/issue/12345
 
 -- golden --
-std: missing skip_fix and vulnerable_at: "net/http"
-std: version issue: introduced and fixed versions must alternate
+modules[0] "std": packages[0] "net/http": at least one of vulnerable_at and skip_fix must be set
+modules[0] "std": versions: introduced and fixed versions must alternate
diff --git a/internal/report/testdata/lint/TestLintOffline/versions_fixed_before_introduced.txtar b/internal/report/testdata/lint/TestLintOffline/versions_fixed_before_introduced.txtar
index fd97113..68034da 100644
--- a/internal/report/testdata/lint/TestLintOffline/versions_fixed_before_introduced.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/versions_fixed_before_introduced.txtar
@@ -23,4 +23,4 @@
     - report: https://go.dev/issue/12345
 
 -- golden --
-std: version issue: range events must be in strictly ascending order (found 1.3.0>=1.2.1)
+modules[0] "std": versions: range events must be in strictly ascending order (found 1.3.0>=1.2.1)
diff --git a/internal/report/testdata/lint/TestLintOffline/versions_overlapping_ranges.txtar b/internal/report/testdata/lint/TestLintOffline/versions_overlapping_ranges.txtar
index b9e6f03..3c64016 100644
--- a/internal/report/testdata/lint/TestLintOffline/versions_overlapping_ranges.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/versions_overlapping_ranges.txtar
@@ -23,4 +23,4 @@
     - report: https://go.dev/issue/12345
 
 -- golden --
-std: version issue: introduced and fixed versions must alternate
+modules[0] "std": versions: introduced and fixed versions must alternate
diff --git a/internal/report/testdata/lint/TestLintOffline/vulnerable_at_out_of_range.txtar b/internal/report/testdata/lint/TestLintOffline/vulnerable_at_out_of_range.txtar
index 66cde17..0425453 100644
--- a/internal/report/testdata/lint/TestLintOffline/vulnerable_at_out_of_range.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/vulnerable_at_out_of_range.txtar
@@ -22,4 +22,4 @@
     - report: https://go.dev/issue/12345
 
 -- golden --
-std: vulnerable_at version 2.0.0 is not inside vulnerable range
+modules[0] "std": vulnerable_at: 2.0.0 is not inside vulnerable range
diff --git a/internal/report/testdata/lint/TestLintOffline/wrong_module_cmd.txtar b/internal/report/testdata/lint/TestLintOffline/wrong_module_cmd.txtar
index 9dcb8a1..5ea4e92 100644
--- a/internal/report/testdata/lint/TestLintOffline/wrong_module_cmd.txtar
+++ b/internal/report/testdata/lint/TestLintOffline/wrong_module_cmd.txtar
@@ -20,4 +20,4 @@
     - report: https://go.dev/issue/12345
 
 -- golden --
-std: "cmd/go" should be in module "cmd", not "std"
+modules[0] "std": packages[0] "cmd/go": must be in module cmd