Repository: google/osv-scanner
Pull request #651 (2023-11-10 09:27:05)

feat: CVSS v4.0 support and replace cvss implementation to comply with the specifications
## Why this PR

[CVSS v4.0](https://www.first.org/cvss/v4-0/) has been released lately, and the OSV will most probably add its support (the first CVSS v4.0 vector known to the FIRST.ORG SIG CVSS has been published [by Palo Alto Networks for the CVE-2023-3282](https://security.paloaltonetworks.com/CVE-2023-3282)).

As a FIRST.ORG SIG CVSS member and [Go CVSS implementation](https://github.com/pandatix/go-cvss) maintainer, I'm looking forward to improve its adoption and understanding in the Open-Source Ecosystem.
Moreover, there exist issues with the currently used CVSS implementation, such as [invalid scoring computation](https://github.com/goark/go-cvss/issues/33), and [CVSS v4.0 is currently not planned for support](https://github.com/goark/go-cvss/issues/37#issuecomment-1706334126).

## What it brings

With the current PR, I provide multiple direct improvements:
- proper CVSS v2.0 scoring computation (only affect the environmental score computation, but has been an unresolved issue for months)
- add support of CVSS v4.0 in the OSV schema
- performance improvements according to [benchmarks](https://github.com/pandatix/go-cvss#comparison)

Given ossf/osv-schema#166 the CVSS v4.0 key will most likely be `CVSS_V4` to align with the previous CVSS versions support.

## Is it breaking ?

For the code, no, but for the Go version, yes :tada:


oliverchang: Thank you for the contribution! Adding @another-rex to review. 
codecov-commenter: ## [Codecov](https://app.codecov.io/gh/google/osv-scanner/pull/651?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) Report
Attention: `8 lines` in your changes are missing coverage. Please review.
> Comparison is base [(`8c5634d`)](https://app.codecov.io/gh/google/osv-scanner/commit/8c5634d41241f1786a8fe0b56a0fe828da97e871?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) 78.50% compared to head [(`63b9aab`)](https://app.codecov.io/gh/google/osv-scanner/pull/651?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) 77.90%.

| [Files](https://app.codecov.io/gh/google/osv-scanner/pull/651?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) | Patch % | Lines |
|---|---|---|
| [internal/output/table.go](https://app.codecov.io/gh/google/osv-scanner/pull/651?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google#diff-aW50ZXJuYWwvb3V0cHV0L3RhYmxlLmdv) | 33.33% | [8 Missing :warning: ](https://app.codecov.io/gh/google/osv-scanner/pull/651?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #651      +/-   ##
==========================================
- Coverage   78.50%   77.90%   -0.60%     
==========================================
  Files          82       82              
  Lines        5890     5898       +8     
==========================================
- Hits         4624     4595      -29     
- Misses       1068     1104      +36     
- Partials      198      199       +1     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/google/osv-scanner/pull/651?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google).

pandatix: Hey @another-rex, thanks for the feedback, much appreciated :)

I stated it could be blocking as despite my implementation is ok to Go 1.18 (in order for ClusterFuzzLite to work), it defines requiring Go 1.21. This imply that a `go mod tidy` would want to update the `go.mod` file (which is indeed related to discussion in #638). As long as you are working on upgrading the Go version this won't be a problem for long, and as it compiles properly it is not blocking ! :tada:


Patch:
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index dce11b1..c7f43eb 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -44,7 +44,7 @@ jobs:
 
       # Initializes the CodeQL tools for scanning.
       - name: Initialize CodeQL
-        uses: github/codeql-action/init@74483a38d39275f33fcff5f35b679b5ca4a26a99 # v2.22.5
+        uses: github/codeql-action/init@407ffafae6a767df3e0230c3df91b6443ae8df75 # v2.22.8
         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@74483a38d39275f33fcff5f35b679b5ca4a26a99 # v2.22.5
+        uses: github/codeql-action/autobuild@407ffafae6a767df3e0230c3df91b6443ae8df75 # v2.22.8
 
       # ℹ️ 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@74483a38d39275f33fcff5f35b679b5ca4a26a99 # v2.22.5
+        uses: github/codeql-action/analyze@407ffafae6a767df3e0230c3df91b6443ae8df75 # v2.22.8
diff --git a/.github/workflows/goreleaser.yml b/.github/workflows/goreleaser.yml
index 720c70b..d7c1fda 100644
--- a/.github/workflows/goreleaser.yml
+++ b/.github/workflows/goreleaser.yml
@@ -1,16 +1,9 @@
 name: Release new version
 
 on:
-  workflow_dispatch:
-    inputs:
-      version:
-        description: 'The version tag to release, (e.g. v1.2.3)'
-        required: true
-        type: string
-      commit:
-        description: 'The commit hash to release'
-        required: true
-        type: string
+  push:
+    tags:
+      - "*" # triggers only if push new tag version, like `v0.8.4` 
 
 permissions:
   contents: read # to fetch code (actions/checkout)
@@ -18,70 +11,6 @@ permissions:
   security-events: write
 
 jobs:
-  osv-scan:
-    uses: ./.github/workflows/osv-scanner-reusable.yml
-    with:
-      # Only scan the top level go.mod file without recursively scanning directories since
-      # this is pipeline is about releasing the go module and binary
-      scan-args: |-
-        --skip-git
-        ./
-
-  lint:
-    name: golangci-lint
-    runs-on: ubuntu-latest
-    steps:
-      - name: Check out code
-        uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
-        with:
-          persist-credentials: false
-          ref: ${{ inputs.commit }}
-      - name: Set up Go
-        uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
-        with:
-          go-version-file: .go-version
-          check-latest: true
-      - name: Run lint action
-        uses: ./.github/workflows/lint-action
-  tests:
-    name: Run unit tests
-    strategy:
-      fail-fast: false
-      matrix:
-        os: [ubuntu-latest, macos-latest, windows-latest]
-    runs-on: ${{ matrix.os }}
-    steps:
-      - name: Check out code
-        uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
-        with:
-          persist-credentials: false
-          ref: ${{ inputs.commit }}
-      - name: Set up Go
-        uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
-        with:
-          go-version-file: .go-version
-          check-latest: true
-      - name: Run test action
-        uses: ./.github/workflows/test-action
-  tag-release:
-    runs-on: ubuntu-latest
-    needs:
-      - lint
-      - tests
-      - osv-scan
-    permissions:
-      contents: write  # to write a tag
-    steps:
-      - name: Create tag
-        uses: actions/github-script@v6
-        with:
-          script: |
-            github.rest.git.createRef({
-              owner: context.repo.owner,
-              repo: context.repo.repo,
-              ref: 'refs/tags/${{ inputs.version }}',
-              sha: '${{ inputs.commit }}'
-            })
   goreleaser:
     outputs:
       hashes: ${{ steps.hash.outputs.hashes }}
@@ -89,8 +18,6 @@ jobs:
       contents: write  # for goreleaser/goreleaser-action to create a GitHub release
       packages: write # for goreleaser/goreleaser-action to publish docker images
     runs-on: ubuntu-latest
-    needs:
-      - tag-release
     env:
       # Required for buildx on docker 19.x
       DOCKER_CLI_EXPERIMENTAL: "enabled"
@@ -140,4 +67,3 @@ jobs:
       base64-subjects: "${{ needs.goreleaser.outputs.hashes }}"
       upload-assets: true # upload to a new release
       draft-release: true # upload to a new draft release
-      upload-tag-name: "${{ inputs.version }}"
diff --git a/.github/workflows/link-check-on-push.yml b/.github/workflows/link-check-on-push.yml
index 62804c8..78098be 100644
--- a/.github/workflows/link-check-on-push.yml
+++ b/.github/workflows/link-check-on-push.yml
@@ -8,7 +8,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
     - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
-    - uses: gaurav-nelson/github-action-markdown-link-check@a996638015fbc9ef96beef1a41bbad7df8e06154
+    - uses: gaurav-nelson/github-action-markdown-link-check@0f074c8562c5a8fed38282b7c741d1970bb1512d
       with:
         use-quiet-mode: "yes"
         base-branch: "main"
diff --git a/.github/workflows/link-check.yml b/.github/workflows/link-check.yml
index a83338c..83ec3a6 100644
--- a/.github/workflows/link-check.yml
+++ b/.github/workflows/link-check.yml
@@ -10,7 +10,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
     - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
-    - uses: gaurav-nelson/github-action-markdown-link-check@a996638015fbc9ef96beef1a41bbad7df8e06154
+    - uses: gaurav-nelson/github-action-markdown-link-check@0f074c8562c5a8fed38282b7c741d1970bb1512d
       with:
         use-quiet-mode: "yes"
 # Documentation available here: https://github.com/marketplace/actions/markdown-link-check
diff --git a/.github/workflows/osv-scanner-reusable-pr.yml b/.github/workflows/osv-scanner-reusable-pr.yml
index 345c663..3deb344 100644
--- a/.github/workflows/osv-scanner-reusable-pr.yml
+++ b/.github/workflows/osv-scanner-reusable-pr.yml
@@ -95,6 +95,6 @@ jobs:
       # Upload the results to GitHub's code scanning dashboard.
       - name: "Upload to code-scanning"
         if: '!cancelled()'
-        uses: github/codeql-action/upload-sarif@74483a38d39275f33fcff5f35b679b5ca4a26a99 # v2.22.5
+        uses: github/codeql-action/upload-sarif@407ffafae6a767df3e0230c3df91b6443ae8df75 # v2.22.8
         with:
           sarif_file: ${{ inputs.results-file-name }}
diff --git a/.github/workflows/osv-scanner-reusable.yml b/.github/workflows/osv-scanner-reusable.yml
index 84a6d70..f1824d3 100644
--- a/.github/workflows/osv-scanner-reusable.yml
+++ b/.github/workflows/osv-scanner-reusable.yml
@@ -78,7 +78,7 @@ jobs:
       # Upload the results to GitHub's code scanning dashboard.
       - name: "Upload to code-scanning"
         if: '!cancelled()'
-        uses: github/codeql-action/upload-sarif@74483a38d39275f33fcff5f35b679b5ca4a26a99 # v2.22.5
+        uses: github/codeql-action/upload-sarif@407ffafae6a767df3e0230c3df91b6443ae8df75 # v2.22.8
         with:
           sarif_file: ${{ inputs.results-file-name }}
 
diff --git a/.github/workflows/prerelease-check.yml b/.github/workflows/prerelease-check.yml
new file mode 100644
index 0000000..77ed541
--- /dev/null
+++ b/.github/workflows/prerelease-check.yml
@@ -0,0 +1,81 @@
+name: Pre-release check
+
+on:
+  workflow_dispatch:
+    inputs:
+      version:
+        description: 'The version tag to release, (e.g. v1.2.3)'
+        required: true
+        type: string
+      commit:
+        description: 'The commit hash to release'
+        required: true
+        type: string
+
+permissions:
+  contents: read # to fetch code (actions/checkout)
+  # Require writing security events to upload SARIF file to security tab
+  security-events: write
+
+jobs:
+  osv-scan:
+    uses: ./.github/workflows/osv-scanner-reusable.yml
+    with:
+      # Only scan the top level go.mod file without recursively scanning directories since
+      # this is pipeline is about releasing the go module and binary
+      scan-args: |-
+        --skip-git
+        ./
+
+  lint:
+    name: golangci-lint
+    runs-on: ubuntu-latest
+    steps:
+      - name: Check out code
+        uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+        with:
+          persist-credentials: false
+          ref: ${{ inputs.commit }}
+      - name: Set up Go
+        uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
+        with:
+          go-version-file: .go-version
+          check-latest: true
+      - name: Run lint action
+        uses: ./.github/workflows/lint-action
+  tests:
+    name: Run unit tests
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ubuntu-latest, macos-latest, windows-latest]
+    runs-on: ${{ matrix.os }}
+    steps:
+      - name: Check out code
+        uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+        with:
+          persist-credentials: false
+          ref: ${{ inputs.commit }}
+      - name: Set up Go
+        uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
+        with:
+          go-version-file: .go-version
+          check-latest: true
+      - name: Run test action
+        uses: ./.github/workflows/test-action
+  release-helper:
+    runs-on: ubuntu-latest
+    needs:
+      - lint
+      - tests
+      - osv-scan
+    steps:
+      - name: Print Scripts
+        env:
+          OUTPUT: |
+            git fetch upstream &&
+            git tag ${{ inputs.version }} ${{ inputs.commit }} &&
+            git push upstream ${{ inputs.version }}
+        shell: bash
+        run: |
+          echo $OUTPUT
\ No newline at end of file
diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml
index 293b2aa..b2554c8 100644
--- a/.github/workflows/scorecards.yml
+++ b/.github/workflows/scorecards.yml
@@ -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@74483a38d39275f33fcff5f35b679b5ca4a26a99 # v2.22.5
+        uses: github/codeql-action/upload-sarif@407ffafae6a767df3e0230c3df91b6443ae8df75 # v2.22.8
         with:
           sarif_file: results.sarif
diff --git a/cmd/osv-scanner/callanalysis_parser.go b/cmd/osv-scanner/callanalysis_parser.go
new file mode 100644
index 0000000..2837fde
--- /dev/null
+++ b/cmd/osv-scanner/callanalysis_parser.go
@@ -0,0 +1,32 @@
+package main
+
+var stableCallAnalysisStates = map[string]bool{
+	"go":   true,
+	"rust": false,
+}
+
+// Creates a map to record if languages are enabled or disabled for call analysis.
+func createCallAnalysisStates(enabledCallAnalysis []string, disabledCallAnalysis []string) map[string]bool {
+	callAnalysisStates := make(map[string]bool)
+
+	for _, language := range enabledCallAnalysis {
+		callAnalysisStates[language] = true
+	}
+
+	for _, language := range disabledCallAnalysis {
+		callAnalysisStates[language] = false
+	}
+
+	enableAll, containsAll := callAnalysisStates["all"]
+	for language, isStable := range stableCallAnalysisStates {
+		if _, exists := callAnalysisStates[language]; !exists {
+			callAnalysisStates[language] = isStable || enableAll
+		}
+		if containsAll && !enableAll {
+			callAnalysisStates[language] = false
+		}
+	}
+	delete(callAnalysisStates, "all")
+
+	return callAnalysisStates
+}
diff --git a/cmd/osv-scanner/main.go b/cmd/osv-scanner/main.go
index 7447042..e5bcfb0 100644
--- a/cmd/osv-scanner/main.go
+++ b/cmd/osv-scanner/main.go
@@ -99,7 +99,7 @@ func run(args []string, stdout, stderr io.Writer) int {
 			},
 			&cli.BoolFlag{
 				Name:  "experimental-call-analysis",
-				Usage: "attempt call analysis on code to detect only active vulnerabilities",
+				Usage: "[Deprecated] attempt call analysis on code to detect only active vulnerabilities",
 				Value: false,
 			},
 			&cli.BoolFlag{
@@ -107,6 +107,14 @@ func run(args []string, stdout, stderr io.Writer) int {
 				Usage: "also scan files that would be ignored by .gitignore",
 				Value: false,
 			},
+			&cli.StringSliceFlag{
+				Name:  "call-analysis",
+				Usage: "attempt call analysis on code to detect only active vulnerabilities",
+			},
+			&cli.StringSliceFlag{
+				Name:  "no-call-analysis",
+				Usage: "disables call graph analysis",
+			},
 			&cli.BoolFlag{
 				Name:  "experimental-local-db",
 				Usage: "checks for vulnerabilities using local databases",
@@ -124,9 +132,13 @@ func run(args []string, stdout, stderr io.Writer) int {
 				Name:  "experimental-all-packages",
 				Usage: "when json output is selected, prints all packages",
 			},
+			&cli.BoolFlag{
+				Name:  "experimental-licenses-summary",
+				Usage: "report a license summary, implying the --experimental-all-packages flag",
+			},
 			&cli.StringSliceFlag{
 				Name:  "experimental-licenses",
-				Usage: "report on licenses",
+				Usage: "report on licenses based on an allowlist",
 			},
 		},
 		ArgsUsage: "[directory1 directory2...]",
@@ -155,10 +167,29 @@ func run(args []string, stdout, stderr io.Writer) int {
 				}
 			}
 
+			if context.Bool("experimental-licenses-summary") && context.IsSet("experimental-licenses") {
+				return fmt.Errorf("--experimental-licenses-summary and --experimental-licenses flags cannot be set")
+			}
+			allowlist := context.StringSlice("experimental-licenses")
+			if context.IsSet("experimental-licenses") &&
+				(len(allowlist) == 0 ||
+					(len(allowlist) == 1 && allowlist[0] == "")) {
+				return fmt.Errorf("--experimental-licenses requires at least one value")
+			}
+			// TODO: verify that the licenses they passed in are indeed spdx.
+
 			if r, err = reporter.New(format, stdout, stderr, termWidth); err != nil {
 				return err
 			}
 
+			var callAnalysisStates map[string]bool
+			if context.IsSet("experimental-call-analysis") {
+				callAnalysisStates = createCallAnalysisStates([]string{"all"}, context.StringSlice("no-call-analysis"))
+				r.PrintText("Warning: the experimental-call-analysis flag has been replaced. Please use the call-analysis and no-call-analysis flags instead.\n")
+			} else {
+				callAnalysisStates = createCallAnalysisStates(context.StringSlice("call-analysis"), context.StringSlice("no-call-analysis"))
+			}
+
 			vulnResult, err := osvscanner.DoScan(osvscanner.ScannerActions{
 				LockfilePaths:        context.StringSlice("lockfile"),
 				SBOMPaths:            context.StringSlice("sbom"),
@@ -168,13 +199,18 @@ func run(args []string, stdout, stderr io.Writer) int {
 				NoIgnore:             context.Bool("no-ignore"),
 				ConfigOverridePath:   context.String("config"),
 				DirectoryPaths:       context.Args().Slice(),
+				CallAnalysisStates:   callAnalysisStates,
 				ExperimentalScannerActions: osvscanner.ExperimentalScannerActions{
-					LocalDBPath:           context.String("experimental-local-db-path"),
-					CallAnalysis:          context.Bool("experimental-call-analysis"),
-					CompareLocally:        context.Bool("experimental-local-db"),
-					CompareOffline:        context.Bool("experimental-offline"),
-					ShowAllPackages:       context.Bool("experimental-all-packages"),
-					ScanLicenses:          context.IsSet("experimental-licenses"),
+					LocalDBPath:    context.String("experimental-local-db-path"),
+					CompareLocally: context.Bool("experimental-local-db"),
+					CompareOffline: context.Bool("experimental-offline"),
+					// License summary mode causes all
+					// packages to appear in the json as
+					// every package has a license - even
+					// if it's just the UNKNOWN license.
+					ShowAllPackages: context.Bool("experimental-all-packages") ||
+						context.Bool("experimental-licenses-summary"),
+					ScanLicensesSummary:   context.Bool("experimental-licenses-summary"),
 					ScanLicensesAllowlist: context.StringSlice("experimental-licenses"),
 				},
 			}, r)
diff --git a/docs/experimental.md b/docs/experimental.md
index bb2b77e..f2fdd5c 100644
--- a/docs/experimental.md
+++ b/docs/experimental.md
@@ -20,46 +20,6 @@ nav_order: 8
 {: .note }
 Features and flags with the `experimental` prefix might change or be removed with only a minor version update.
 
-## Scanning with call analysis  
-
-Call stack analysis can be performed on some languages to check if the 
-vulnerable code is actually being executed by your project. If the code
-is not being executed, these vulnerabilities will be marked as unexecuted.
-
-To enable call analysis, call OSV-Scanner with the `--experimental-call-analysis` flag.
-
-### Call analysis in Go
-
-OSV-Scanner uses the `govulncheck` library to analyze Go source code to identify called vulnerable functions.
-
-#### Additional Dependencies
-
-`go` compiler needs to be installed and available on `PATH`    
-
-### Call analysis in Rust
-
-OSV-Scanner compiles Rust source code and analyzes the output binary's DWARF debug information to identify called vulnerable functions.
-
-#### Additional Dependencies
-
-Rust toolchain (including `cargo`) that can compile the source code being scanned needs to be installed and available on `PATH`.
-
-The installed Rust toolchain must be capable of compiling every crate/target in the scanned code, for code with
-a lot of dependencies this will take a few minutes.
-
-### Limitations
-
-Current implementation has a few limitations:
-
-- Does not support dependencies on proc-macros (Tracked in [#464](https://github.com/google/osv-scanner/issues/464))
-- Does not support any dependencies that are dynamically linked
-- Does not support dependencies that link external non-rust code
-
-### Example
-```bash
-osv-scanner --experimental-call-analysis ./my/project/path
-```
-
 ## Offline mode
 
 OSV-Scanner now supports offline scanning as an experimental feature. Offline scanning checks your project against a local database instead of calling the OSV.dev API.
@@ -128,3 +88,38 @@ Set the location of your manually downloaded database by following the instructi
 ### Limitations
 
 1. Commit level scanning is not supported. 
+
+## License scanning
+
+OSV-Scanner supports license checking as an experimental feature. The data comes from the [deps.dev API](https://docs.deps.dev/api/).
+
+### License summary
+
+If you want a summary of your dependencies licenses, use the `--experimental-licenses-summary` flag:
+
+```bash
+osv-scanner --experimental-licenses-summary path/to/repository`
+```
+
+### License violations
+
+To set an allowed license list and see the details of packages that do not conform, use the `--experimental-licenses` flag:
+
+```bash
+osv-scanner --experimental-licenses="comma-separated list of allowed licenses" path/to/directory
+```
+Include your allowed licenses as a comma-separated list. OSV-Scanner recognizes licenses in SPDX format. Please indicate your allowed licenses using [SPDX license](https://spdx.org/licenses/) identifiers. 
+
+#### License violations example
+
+If you wanted to allow the following licenses:
+
+- [BSD 3-Clause "New" or "Revised" License](https://spdx.org/licenses/BSD-3-Clause.html)
+- [Apache License 2.0](https://spdx.org/licenses/Apache-2.0.html)
+- [MIT](https://spdx.org/licenses/MIT.html)
+
+Your command would be in this form:
+
+```bash
+osv-scanner --experimental-licenses="BSD-3-Clause,Apache-2.0,MIT" path/to/directory
+```
\ No newline at end of file
diff --git a/docs/usage.md b/docs/usage.md
index db8619e..ac34a50 100644
--- a/docs/usage.md
+++ b/docs/usage.md
@@ -145,3 +145,46 @@ OSV-Scanner's C/C++ support is based on commit-level data. OSV's commit-level da
 ### Vendored dependencies
 
 Vendored dependencies have been directly copied into the project folder, but do not retain their Git histories. OSV-Scanner uses OSV's [determineversion API](https://google.github.io/osv.dev/post-v1-determineversion/) to estimate each dependency's version (and associated Git commit). Vulnerabilities for the estimated version are returned. This process requires no additional work from the user. Run OSV-Scanner as you normally would. 
+
+## Scanning with call analysis
+
+Call stack analysis can be performed on some languages to check if the 
+vulnerable code is actually being executed by your project. If the code
+is not being executed, these vulnerabilities will be marked as unexecuted.
+
+To enable call analysis in all languages, call OSV-Scanner with the `--call-analysis=all` flag. By default, call analysis in Go is enabled, but you can disable it using the `--no-call-analysis=go` flag.
+
+### Call analysis in Go
+
+OSV-Scanner uses the [`govulncheck`](https://pkg.go.dev/golang.org/x/vuln/cmd/govulncheck) library to analyze Go source code to identify called vulnerable functions.
+
+#### Additional Dependencies
+
+`go` compiler needs to be installed and available on `PATH`
+
+### Call analysis in Rust
+
+Experimental {: .label }
+Call analysis in Rust is still considered experimental. 
+
+OSV-Scanner compiles Rust source code and analyzes the output binary's DWARF debug information to identify called vulnerable functions.
+
+#### Additional Dependencies
+
+Rust toolchain (including `cargo`) that can compile the source code being scanned needs to be installed and available on `PATH`.
+
+The installed Rust toolchain must be capable of compiling every crate/target in the scanned code, for code with
+a lot of dependencies this will take a few minutes.
+
+### Limitations
+
+Current implementation has a few limitations:
+
+- Does not support dependencies on proc-macros (Tracked in [#464](https://github.com/google/osv-scanner/issues/464))
+- Does not support any dependencies that are dynamically linked
+- Does not support dependencies that link external non-rust code
+
+### Example
+```bash
+osv-scanner --call-analysis=rust --no-call-analysis=go ./my/project/path
+```
diff --git a/go.mod b/go.mod
index b47c9dd..e88c28e 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
 module github.com/google/osv-scanner
 
-go 1.20
+go 1.21
 
 require (
 	deps.dev/api/v3alpha v0.0.0-20231114023923-e40c4d5c34e5
@@ -8,13 +8,13 @@ require (
 	github.com/CycloneDX/cyclonedx-go v0.7.2
 	github.com/go-git/go-billy/v5 v5.5.0
 	github.com/go-git/go-git/v5 v5.10.0
-	github.com/goark/go-cvss v1.6.6
 	github.com/google/go-cmp v0.6.0
 	github.com/hexops/gotextdiff v1.0.3
 	github.com/ianlancetaylor/demangle v0.0.0-20231023195312-e2daf7ba7156
 	github.com/jedib0t/go-pretty/v6 v6.4.9
 	github.com/owenrumney/go-sarif/v2 v2.3.0
 	github.com/package-url/packageurl-go v0.1.2
+	github.com/pandatix/go-cvss v0.6.2
 	github.com/spdx/tools-golang v0.5.3
 	github.com/urfave/cli/v2 v2.25.7
 	golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa
@@ -38,7 +38,6 @@ require (
 	github.com/cyphar/filepath-securejoin v0.2.4 // 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.3.2 // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 	github.com/golang/protobuf v1.5.3 // indirect
 	github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
diff --git a/go.sum b/go.sum
index fe75793..6367f79 100644
--- a/go.sum
+++ b/go.sum
@@ -17,9 +17,12 @@ github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092/go.mod
 github.com/anchore/go-struct-converter v0.0.0-20230627203149-c72ef8859ca9 h1:6COpXWpHbhWM1wgcQN95TdsmrLTba8KQfPgImBXzkjA=
 github.com/anchore/go-struct-converter v0.0.0-20230627203149-c72ef8859ca9/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA=
 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
+github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
 github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
+github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
 github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M=
+github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=
 github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
 github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs=
 github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
@@ -31,20 +34,19 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
+github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
 github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
 github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
 github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
+github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
 github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
 github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
 github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
 github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
 github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
+github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
 github.com/go-git/go-git/v5 v5.10.0 h1:F0x3xXrAWmhwtzoCokU4IMPcBdncG+HAAqi9FcOOjbQ=
 github.com/go-git/go-git/v5 v5.10.0/go.mod h1:1FOZ/pQnqw24ghP2n7cunVl0ON55BsjPYvhWHvZGhoo=
-github.com/goark/errs v1.3.2 h1:ifccNe1aK7Xezt4XVYwHUqalmnfhuphnEvh3FshCReQ=
-github.com/goark/errs v1.3.2/go.mod h1:ZsQucxaDFVfSB8I99j4bxkDRfNOrlKINwg72QMuRWKw=
-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/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -69,20 +71,25 @@ github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4
 github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
 github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
 github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
 github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
 github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
 github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
+github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
 github.com/owenrumney/go-sarif v1.1.1/go.mod h1:dNDiPlF04ESR/6fHlPyq7gHKmrM0sHUvAGjsoh8ZH0U=
 github.com/owenrumney/go-sarif/v2 v2.3.0 h1:wP5yEpI53zr0v5cBmagXzLbHZp9Oylyo3AJDpfLBITs=
 github.com/owenrumney/go-sarif/v2 v2.3.0/go.mod h1:MSqMMx9WqlBSY7pXoOZWgEsVB4FDNfhcaXDA1j6Sr+w=
 github.com/package-url/packageurl-go v0.1.2 h1:0H2DQt6DHd/NeRlVwW4EZ4oEI6Bn40XlNPRqegcxuo4=
 github.com/package-url/packageurl-go v0.1.2/go.mod h1:uQd4a7Rh3ZsVg5j0lNyAfyxIeGde9yrlhjF78GzeW0c=
+github.com/pandatix/go-cvss v0.6.2 h1:TFiHlzUkT67s6UkelHmK6s1INKVUG7nlKYiWWDTITGI=
+github.com/pandatix/go-cvss v0.6.2/go.mod h1:jDXYlQBZrc8nvrMUVVvTG8PhmuShOnKrxP53nOFkt8Q=
 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/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -94,6 +101,7 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
 github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
 github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
 github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
+github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
 github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
@@ -118,6 +126,7 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
 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/terminalstatic/go-xsd-validate v0.1.5 h1:RqpJnf6HGE2CB/lZB1A8BYguk8uRtcvYAPLCF15qguo=
+github.com/terminalstatic/go-xsd-validate v0.1.5/go.mod h1:18lsvYFofBflqCrvo1umpABZ99+GneNTw2kEEc8UPJw=
 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/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
@@ -125,8 +134,11 @@ github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgq
 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/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
 github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
 github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
 github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
 github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
@@ -217,6 +229,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
 gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/internal/output/table.go b/internal/output/table.go
index 3f26023..80d20ce 100644
--- a/internal/output/table.go
+++ b/internal/output/table.go
@@ -10,8 +10,10 @@ import (
 	"sort"
 	"strings"
 
-	v2_metric "github.com/goark/go-cvss/v2/metric"
-	v3_metric "github.com/goark/go-cvss/v3/metric"
+	gocvss20 "github.com/pandatix/go-cvss/20"
+	gocvss30 "github.com/pandatix/go-cvss/30"
+	gocvss31 "github.com/pandatix/go-cvss/31"
+	gocvss40 "github.com/pandatix/go-cvss/40"
 	"golang.org/x/exp/maps"
 
 	"github.com/google/osv-scanner/internal/utility/results"
@@ -155,11 +157,20 @@ func MaxSeverity(group models.GroupInfo, pkg models.PackageVulns) string {
 		for _, severity := range severities {
 			switch severity.Type {
 			case models.SeverityCVSSV2:
-				numericSeverity, _ := v2_metric.NewBase().Decode(severity.Score)
-				maxSeverity = math.Max(maxSeverity, numericSeverity.Score())
+				vec, _ := gocvss20.ParseVector(severity.Score)
+				maxSeverity = math.Max(maxSeverity, vec.BaseScore())
 			case models.SeverityCVSSV3:
-				numericSeverity, _ := v3_metric.NewBase().Decode(severity.Score)
-				maxSeverity = math.Max(maxSeverity, numericSeverity.Score())
+				switch {
+				case strings.HasPrefix(severity.Score, "CVSS:3.0"):
+					vec, _ := gocvss30.ParseVector(severity.Score)
+					maxSeverity = math.Max(maxSeverity, vec.BaseScore())
+				case strings.HasPrefix(severity.Score, "CVSS:3.1"):
+					vec, _ := gocvss31.ParseVector(severity.Score)
+					maxSeverity = math.Max(maxSeverity, vec.BaseScore())
+				}
+			case models.SeverityCVSSV4:
+				vec, _ := gocvss40.ParseVector(severity.Score)
+				maxSeverity = math.Max(maxSeverity, vec.Score())
 			}
 		}
 	}
@@ -173,14 +184,13 @@ func MaxSeverity(group models.GroupInfo, pkg models.PackageVulns) string {
 
 func licenseTableBuilder(outputTable table.Writer, vulnResult *models.VulnerabilityResults) table.Writer {
 	licenseConfig := vulnResult.ExperimentalAnalysisConfig.Licenses
-	if !licenseConfig.Enabled {
-		return outputTable
-	}
-	if len(licenseConfig.Allowlist) == 0 {
+	if licenseConfig.Summary {
 		return licenseSummaryTableBuilder(outputTable, vulnResult)
+	} else if len(licenseConfig.Allowlist) > 0 {
+		return licenseViolationsTableBuilder(outputTable, vulnResult)
+	} else {
+		return outputTable
 	}
-
-	return licenseViolationsTableBuilder(outputTable, vulnResult)
 }
 
 func licenseSummaryTableBuilder(outputTable table.Writer, vulnResult *models.VulnerabilityResults) table.Writer {
diff --git a/internal/sourceanalysis/go.go b/internal/sourceanalysis/go.go
index 4c8d119..d4b8623 100644
--- a/internal/sourceanalysis/go.go
+++ b/internal/sourceanalysis/go.go
@@ -7,6 +7,7 @@ import (
 	"fmt"
 	"io"
 	"os"
+	"os/exec"
 	"path/filepath"
 
 	"github.com/google/osv-scanner/internal/sourceanalysis/govulncheck"
@@ -17,6 +18,13 @@ import (
 )
 
 func goAnalysis(r reporter.Reporter, pkgs []models.PackageVulns, source models.SourceInfo) {
+	cmd := exec.Command("go", "version")
+	_, err := cmd.Output()
+	if err != nil {
+		r.PrintText("Skipping call analysis on Go code since Go is not installed.\n")
+		return
+	}
+
 	vulns, vulnsByID := vulnsFromAllPkgs(pkgs)
 	res, err := runGovulncheck(filepath.Dir(source.Path), vulns)
 	if err != nil {
diff --git a/internal/sourceanalysis/sourceanalysis.go b/internal/sourceanalysis/sourceanalysis.go
index 745b321..1c9f5d8 100644
--- a/internal/sourceanalysis/sourceanalysis.go
+++ b/internal/sourceanalysis/sourceanalysis.go
@@ -25,13 +25,13 @@ func vulnsFromAllPkgs(pkgs []models.PackageVulns) ([]models.Vulnerability, map[s
 }
 
 // Run runs the language specific analyzers on the code given packages and source info
-func Run(r reporter.Reporter, source models.SourceInfo, pkgs []models.PackageVulns) {
+func Run(r reporter.Reporter, source models.SourceInfo, pkgs []models.PackageVulns, callAnalysis map[string]bool) {
 	// GoVulnCheck
-	if source.Type == "lockfile" && filepath.Base(source.Path) == "go.mod" {
+	if source.Type == "lockfile" && filepath.Base(source.Path) == "go.mod" && callAnalysis["go"] {
 		goAnalysis(r, pkgs, source)
 	}
 
-	if source.Type == "lockfile" && filepath.Base(source.Path) == "Cargo.lock" {
+	if source.Type == "lockfile" && filepath.Base(source.Path) == "Cargo.lock" && callAnalysis["rust"] {
 		rustAnalysis(r, pkgs, source)
 	}
 }
diff --git a/pkg/grouper/grouper.go b/pkg/grouper/grouper.go
index 2974042..7e42b27 100644
--- a/pkg/grouper/grouper.go
+++ b/pkg/grouper/grouper.go
@@ -51,8 +51,10 @@ func Group(vulns []IDAliases) []models.GroupInfo {
 
 	// Extract groups into the final result structure.
 	extractedGroups := map[int][]string{}
+	extractedAliases := map[int][]string{}
 	for i, gid := range groups {
 		extractedGroups[gid] = append(extractedGroups[gid], vulns[i].ID)
+		extractedAliases[gid] = append(extractedAliases[gid], vulns[i].Aliases...)
 	}
 
 	// Sort by group ID to maintain stable order for tests.
@@ -63,7 +65,15 @@ func Group(vulns []IDAliases) []models.GroupInfo {
 	for _, key := range sortedKeys {
 		// Sort the strings so they are always in the same order
 		sort.Strings(extractedGroups[key])
-		result = append(result, models.GroupInfo{IDs: extractedGroups[key]})
+
+		// Add IDs to aliases
+		extractedAliases[key] = append(extractedAliases[key], extractedGroups[key]...)
+
+		// Dedup entries
+		sort.Strings(extractedAliases[key])
+		extractedAliases[key] = slices.Compact(extractedAliases[key])
+
+		result = append(result, models.GroupInfo{IDs: extractedGroups[key], Aliases: extractedAliases[key]})
 	}
 
 	return result
diff --git a/pkg/models/constants.go b/pkg/models/constants.go
index 7c392fe..b97ebd3 100644
--- a/pkg/models/constants.go
+++ b/pkg/models/constants.go
@@ -61,6 +61,7 @@ type SeverityType string
 const (
 	SeverityCVSSV2 SeverityType = "CVSS_V2"
 	SeverityCVSSV3 SeverityType = "CVSS_V3"
+	SeverityCVSSV4 SeverityType = "CVSS_V4"
 )
 
 type RangeType string
diff --git a/pkg/models/results.go b/pkg/models/results.go
index 81425aa..4aba0d5 100644
--- a/pkg/models/results.go
+++ b/pkg/models/results.go
@@ -15,16 +15,11 @@ type VulnerabilityResults struct {
 // ExperimentalAnalysisConfig is an experimental type intended to contain the
 // types of analysis performed on packages found by the scanner.
 type ExperimentalAnalysisConfig struct {
-	CallAnalysis ExperimentalCallAnalysisConfig `json:"call_analysis"`
-	Licenses     ExperimentalLicenseConfig      `json:"licenses"`
-}
-
-type ExperimentalCallAnalysisConfig struct {
-	Enabled bool `json:"enabled"`
+	Licenses ExperimentalLicenseConfig `json:"licenses"`
 }
 
 type ExperimentalLicenseConfig struct {
-	Enabled   bool      `json:"enabled"`
+	Summary   bool      `json:"summary"`
 	Allowlist []License `json:"allowlist"`
 }
 
@@ -108,6 +103,8 @@ type PackageVulns struct {
 type GroupInfo struct {
 	// IDs expected to be sorted in alphanumeric order
 	IDs []string `json:"ids"`
+	// Aliases include all aliases and IDs
+	Aliases []string `json:"aliases"`
 	// Map of Vulnerability IDs to AnalysisInfo
 	ExperimentalAnalysis map[string]AnalysisInfo `json:"experimentalAnalysis,omitempty"`
 }
@@ -170,5 +167,5 @@ type PackageInfo struct {
 	Name      string `json:"name"`
 	Version   string `json:"version"`
 	Ecosystem string `json:"ecosystem"`
-	Commit    string `json:"commit"`
+	Commit    string `json:"commit,omitempty"`
 }
diff --git a/pkg/osvscanner/fixtures/filter/all/configs/b/osv-scanner.toml b/pkg/osvscanner/fixtures/filter/all/configs/b/osv-scanner.toml
index 5c4e697..cb205e2 100644
--- a/pkg/osvscanner/fixtures/filter/all/configs/b/osv-scanner.toml
+++ b/pkg/osvscanner/fixtures/filter/all/configs/b/osv-scanner.toml
@@ -11,6 +11,6 @@ reason = "Ignore 2"
 # Alias of GHSA-xrjj-mj9h-534m
 
 [[IgnoredVulns]]
-id = "GO-2023-1571"
+id = "CVE-2022-41723"
 reason = "Ignore 3"
-# Alias of GHSA-vvpx-j8f3-3w6h
+# Alias of GHSA-vvpx-j8f3-3w6h and GO-2023-1571
diff --git a/pkg/osvscanner/fixtures/filter/all/input.json b/pkg/osvscanner/fixtures/filter/all/input.json
index 0188dfe..9335336 100644
--- a/pkg/osvscanner/fixtures/filter/all/input.json
+++ b/pkg/osvscanner/fixtures/filter/all/input.json
@@ -149,6 +149,10 @@
               "ids": [
                 "GHSA-mc8h-8q98-g5hr",
                 "RUSTSEC-2023-0018"
+              ],
+              "aliases": [
+                "GHSA-mc8h-8q98-g5hr",
+                "RUSTSEC-2023-0018"
               ]
             }
           ]
@@ -393,6 +397,10 @@
               "ids": [
                 "GHSA-wcg3-cvx6-7396",
                 "RUSTSEC-2020-0071"
+              ],
+              "aliases": [
+                "GHSA-wcg3-cvx6-7396",
+                "RUSTSEC-2020-0071"
               ]
             }
           ]
@@ -867,17 +875,29 @@
               "ids": [
                 "GHSA-fxg5-wq6x-vr4w",
                 "GO-2023-1495"
+              ],
+              "aliases": [
+                "GHSA-fxg5-wq6x-vr4w",
+                "GO-2023-1495"
               ]
             },
             {
               "ids": [
                 "GO-2022-1144"
+              ],
+              "aliases": [
+                "GO-2022-1144"
               ]
             },
             {
               "ids": [
                 "GHSA-vvpx-j8f3-3w6h",
                 "GO-2023-1571"
+              ],
+              "aliases": [
+                "GHSA-vvpx-j8f3-3w6h",
+                "GO-2023-1571",
+                "CVE-2022-41723"
               ]
             }
           ]
@@ -1021,11 +1041,17 @@
             {
               "ids": [
                 "GHSA-mrrw-grhq-86gf"
+              ],
+              "aliases": [
+                "GHSA-mrrw-grhq-86gf"
               ]
             },
             {
               "ids": [
                 "RUSTSEC-2023-0015"
+              ],
+              "aliases": [
+                "RUSTSEC-2023-0015"
               ]
             }
           ]
@@ -1173,6 +1199,10 @@
               "ids": [
                 "GHSA-mc8h-8q98-g5hr",
                 "RUSTSEC-2023-0018"
+              ],
+              "aliases": [
+                "GHSA-mc8h-8q98-g5hr",
+                "RUSTSEC-2023-0018"
               ]
             }
           ]
@@ -1417,6 +1447,10 @@
               "ids": [
                 "GHSA-wcg3-cvx6-7396",
                 "RUSTSEC-2020-0071"
+              ],
+              "aliases": [
+                "GHSA-wcg3-cvx6-7396",
+                "RUSTSEC-2020-0071"
               ]
             }
           ]
diff --git a/pkg/osvscanner/fixtures/filter/all/want.json b/pkg/osvscanner/fixtures/filter/all/want.json
index 8932c42..786594f 100644
--- a/pkg/osvscanner/fixtures/filter/all/want.json
+++ b/pkg/osvscanner/fixtures/filter/all/want.json
@@ -1,3 +1,12 @@
 {
-  "results": []
+  "results": [],
+  "experimental_config": {
+    "call_analysis": {
+      "enabled": false
+    },
+    "licenses": {
+      "enabled": false,
+      "allowlist": null
+    }
+  }
 }
diff --git a/pkg/osvscanner/fixtures/filter/none/input.json b/pkg/osvscanner/fixtures/filter/none/input.json
index 32716a6..c5f6f25 100644
--- a/pkg/osvscanner/fixtures/filter/none/input.json
+++ b/pkg/osvscanner/fixtures/filter/none/input.json
@@ -149,6 +149,10 @@
               "ids": [
                 "GHSA-mc8h-8q98-g5hr",
                 "RUSTSEC-2023-0018"
+              ],
+              "aliases": [
+                "GHSA-mc8h-8q98-g5hr",
+                "RUSTSEC-2023-0018"
               ]
             }
           ]
@@ -393,6 +397,10 @@
               "ids": [
                 "GHSA-wcg3-cvx6-7396",
                 "RUSTSEC-2020-0071"
+              ],
+              "aliases": [
+                "GHSA-wcg3-cvx6-7396",
+                "RUSTSEC-2020-0071"
               ]
             }
           ]
@@ -867,17 +875,28 @@
               "ids": [
                 "GHSA-fxg5-wq6x-vr4w",
                 "GO-2023-1495"
+              ],
+              "aliases": [
+                "GHSA-fxg5-wq6x-vr4w",
+                "GO-2023-1495"
               ]
             },
             {
               "ids": [
                 "GO-2022-1144"
+              ],
+              "aliases": [
+                "GO-2022-1144"
               ]
             },
             {
               "ids": [
                 "GHSA-vvpx-j8f3-3w6h",
                 "GO-2023-1571"
+              ],
+              "aliases": [
+                "GHSA-vvpx-j8f3-3w6h",
+                "GO-2023-1571"
               ]
             }
           ]
@@ -1021,11 +1040,17 @@
             {
               "ids": [
                 "GHSA-mrrw-grhq-86gf"
+              ],
+              "aliases": [
+                "GHSA-mrrw-grhq-86gf"
               ]
             },
             {
               "ids": [
                 "RUSTSEC-2023-0015"
+              ],
+              "aliases": [
+                "RUSTSEC-2023-0015"    
               ]
             }
           ]
@@ -1173,6 +1198,10 @@
               "ids": [
                 "GHSA-mc8h-8q98-g5hr",
                 "RUSTSEC-2023-0018"
+              ],
+              "aliases": [
+                "GHSA-mc8h-8q98-g5hr",
+                "RUSTSEC-2023-0018"
               ]
             }
           ]
@@ -1417,6 +1446,10 @@
               "ids": [
                 "GHSA-wcg3-cvx6-7396",
                 "RUSTSEC-2020-0071"
+              ],
+              "aliases": [
+                "GHSA-wcg3-cvx6-7396",
+                "RUSTSEC-2020-0071"
               ]
             }
           ]
diff --git a/pkg/osvscanner/fixtures/filter/none/want.json b/pkg/osvscanner/fixtures/filter/none/want.json
index 32716a6..81c730b 100644
--- a/pkg/osvscanner/fixtures/filter/none/want.json
+++ b/pkg/osvscanner/fixtures/filter/none/want.json
@@ -14,11 +14,10 @@
           },
           "vulnerabilities": [
             {
-              "schema_version": "1.4.0",
-              "id": "GHSA-mc8h-8q98-g5hr",
               "modified": "2023-02-24T16:23:59Z",
               "published": "2023-02-24T16:23:59Z",
-              "aliases": null,
+              "schema_version": "1.4.0",
+              "id": "GHSA-mc8h-8q98-g5hr",
               "summary": "Race Condition Enabling Link Following and Time-of-check Time-of-use (TOCTOU) Race Condition in remove_dir_all",
               "details": "The `remove_dir_all` crate is a Rust library that offers additional features over the Rust standard library `fs::remove_dir_all` function. It suffers the same class of failure as the code it was layering over: TOCTOU race conditions, with the ability to cause arbitrary paths to be deleted by substituting a symlink for a path after the type of the path was checked.\n\nThanks to the Rust security team for identifying the problem and alerting us to it.",
               "affected": [
@@ -76,10 +75,10 @@
               }
             },
             {
-              "schema_version": "1.4.0",
-              "id": "RUSTSEC-2023-0018",
               "modified": "2023-03-04T21:50:30Z",
               "published": "2023-02-24T12:00:00Z",
+              "schema_version": "1.4.0",
+              "id": "RUSTSEC-2023-0018",
               "aliases": [
                 "GHSA-mc8h-8q98-g5hr"
               ],
@@ -149,6 +148,10 @@
               "ids": [
                 "GHSA-mc8h-8q98-g5hr",
                 "RUSTSEC-2023-0018"
+              ],
+              "aliases": [
+                "GHSA-mc8h-8q98-g5hr",
+                "RUSTSEC-2023-0018"
               ]
             }
           ]
@@ -161,10 +164,10 @@
           },
           "vulnerabilities": [
             {
-              "schema_version": "1.4.0",
-              "id": "GHSA-wcg3-cvx6-7396",
               "modified": "2022-12-06T00:16:25Z",
               "published": "2021-08-25T20:56:46Z",
+              "schema_version": "1.4.0",
+              "id": "GHSA-wcg3-cvx6-7396",
               "aliases": [
                 "CVE-2020-26235"
               ],
@@ -263,10 +266,10 @@
               }
             },
             {
-              "schema_version": "1.4.0",
-              "id": "RUSTSEC-2020-0071",
               "modified": "2023-02-08T15:06:38Z",
               "published": "2020-11-18T12:00:00Z",
+              "schema_version": "1.4.0",
+              "id": "RUSTSEC-2020-0071",
               "aliases": [
                 "CVE-2020-26235"
               ],
@@ -393,6 +396,10 @@
               "ids": [
                 "GHSA-wcg3-cvx6-7396",
                 "RUSTSEC-2020-0071"
+              ],
+              "aliases": [
+                "GHSA-wcg3-cvx6-7396",
+                "RUSTSEC-2020-0071"
               ]
             }
           ]
@@ -413,10 +420,10 @@
           },
           "vulnerabilities": [
             {
-              "schema_version": "1.4.0",
-              "id": "GHSA-fxg5-wq6x-vr4w",
               "modified": "2023-01-24T18:56:46Z",
               "published": "2023-01-14T00:30:23Z",
+              "schema_version": "1.4.0",
+              "id": "GHSA-fxg5-wq6x-vr4w",
               "aliases": [
                 "CVE-2022-41721"
               ],
@@ -503,15 +510,14 @@
               }
             },
             {
-              "schema_version": "1.4.0",
-              "id": "GO-2023-1495",
               "modified": "2023-01-31T21:39:17Z",
               "published": "2023-01-13T22:39:40Z",
+              "schema_version": "1.4.0",
+              "id": "GO-2023-1495",
               "aliases": [
                 "CVE-2022-41721",
                 "GHSA-fxg5-wq6x-vr4w"
               ],
-              "summary": "",
               "details": "A request smuggling attack is possible when using MaxBytesHandler.\n\nWhen using MaxBytesHandler, the body of an HTTP request is not fully consumed. When the server attempts to read HTTP2 frames from the connection, it will instead be reading the body of the HTTP request, which could be attacker-manipulated to represent arbitrary HTTP2 requests.",
               "affected": [
                 {
@@ -562,15 +568,14 @@
               ]
             },
             {
-              "schema_version": "1.4.0",
-              "id": "GO-2022-1144",
               "modified": "2023-01-31T21:39:15Z",
               "published": "2022-12-08T19:01:21Z",
+              "schema_version": "1.4.0",
+              "id": "GO-2022-1144",
               "aliases": [
                 "CVE-2022-41717",
                 "GHSA-xrjj-mj9h-534m"
               ],
-              "summary": "",
               "details": "An attacker can cause excessive memory growth in a Go server accepting HTTP/2 requests.\n\nHTTP/2 server connections contain a cache of HTTP header keys sent by the client. While the total number of entries in this cache is capped, an attacker sending very large keys can cause the server to allocate approximately 64 MiB per open connection.",
               "affected": [
                 {
@@ -678,10 +683,10 @@
               ]
             },
             {
-              "schema_version": "1.4.0",
-              "id": "GHSA-vvpx-j8f3-3w6h",
               "modified": "2023-03-09T21:20:44Z",
               "published": "2023-02-17T14:00:02Z",
+              "schema_version": "1.4.0",
+              "id": "GHSA-vvpx-j8f3-3w6h",
               "aliases": [
                 "CVE-2022-41723"
               ],
@@ -753,15 +758,14 @@
               }
             },
             {
-              "schema_version": "1.4.0",
-              "id": "GO-2023-1571",
               "modified": "2023-02-22T20:13:12Z",
               "published": "2023-02-16T22:31:36Z",
+              "schema_version": "1.4.0",
+              "id": "GO-2023-1571",
               "aliases": [
                 "CVE-2022-41723",
                 "GHSA-vvpx-j8f3-3w6h"
               ],
-              "summary": "",
               "details": "A maliciously crafted HTTP/2 stream could cause excessive CPU consumption in the HPACK decoder, sufficient to cause a denial of service from a small number of small requests.",
               "affected": [
                 {
@@ -867,17 +871,28 @@
               "ids": [
                 "GHSA-fxg5-wq6x-vr4w",
                 "GO-2023-1495"
+              ],
+              "aliases": [
+                "GHSA-fxg5-wq6x-vr4w",
+                "GO-2023-1495"
               ]
             },
             {
               "ids": [
                 "GO-2022-1144"
+              ],
+              "aliases": [
+                "GO-2022-1144"
               ]
             },
             {
               "ids": [
                 "GHSA-vvpx-j8f3-3w6h",
                 "GO-2023-1571"
+              ],
+              "aliases": [
+                "GHSA-vvpx-j8f3-3w6h",
+                "GO-2023-1571"
               ]
             }
           ]
@@ -898,11 +913,10 @@
           },
           "vulnerabilities": [
             {
-              "schema_version": "1.4.0",
-              "id": "GHSA-mrrw-grhq-86gf",
               "modified": "2023-02-28T20:30:10Z",
               "published": "2023-02-28T20:30:10Z",
-              "aliases": null,
+              "schema_version": "1.4.0",
+              "id": "GHSA-mrrw-grhq-86gf",
               "summary": "Ascii (crate) allows out-of-bounds array indexing in safe code",
               "details": "Affected version of this crate had implementation of `From\u003c\u0026mut AsciiStr\u003e` for `\u0026mut [u8]` and `\u0026mut str`. This can result in out-of-bounds array indexing in safe code.\n\nThe flaw was corrected in commit [8a6c779](https://github.com/tomprogrammer/rust-ascii/pull/63/commits/8a6c7798c202766bd57d70fb8d12739dd68fb9dc) by removing those impls.\n",
               "affected": [
@@ -957,11 +971,10 @@
               }
             },
             {
-              "schema_version": "1.4.0",
-              "id": "RUSTSEC-2023-0015",
               "modified": "2023-02-25T15:13:09Z",
               "published": "2023-02-25T12:00:00Z",
-              "aliases": null,
+              "schema_version": "1.4.0",
+              "id": "RUSTSEC-2023-0015",
               "summary": "Ascii allows out-of-bounds array indexing in safe code",
               "details": "Affected version of this crate had implementation of `From\u003c\u0026mut AsciiStr\u003e` for `\u0026mut [u8]` and `\u0026mut str`. This can result in out-of-bounds array indexing in safe code.\n\nThe flaw was corrected in commit [8a6c779](https://github.com/tomprogrammer/rust-ascii/pull/63/commits/8a6c7798c202766bd57d70fb8d12739dd68fb9dc) by removing those impls.",
               "affected": [
@@ -1021,11 +1034,17 @@
             {
               "ids": [
                 "GHSA-mrrw-grhq-86gf"
+              ],
+              "aliases": [
+                "GHSA-mrrw-grhq-86gf"
               ]
             },
             {
               "ids": [
                 "RUSTSEC-2023-0015"
+              ],
+              "aliases": [
+                "RUSTSEC-2023-0015"
               ]
             }
           ]
@@ -1038,11 +1057,10 @@
           },
           "vulnerabilities": [
             {
-              "schema_version": "1.4.0",
-              "id": "GHSA-mc8h-8q98-g5hr",
               "modified": "2023-02-24T16:23:59Z",
               "published": "2023-02-24T16:23:59Z",
-              "aliases": null,
+              "schema_version": "1.4.0",
+              "id": "GHSA-mc8h-8q98-g5hr",
               "summary": "Race Condition Enabling Link Following and Time-of-check Time-of-use (TOCTOU) Race Condition in remove_dir_all",
               "details": "The `remove_dir_all` crate is a Rust library that offers additional features over the Rust standard library `fs::remove_dir_all` function. It suffers the same class of failure as the code it was layering over: TOCTOU race conditions, with the ability to cause arbitrary paths to be deleted by substituting a symlink for a path after the type of the path was checked.\n\nThanks to the Rust security team for identifying the problem and alerting us to it.",
               "affected": [
@@ -1100,10 +1118,10 @@
               }
             },
             {
-              "schema_version": "1.4.0",
-              "id": "RUSTSEC-2023-0018",
               "modified": "2023-03-04T21:50:30Z",
               "published": "2023-02-24T12:00:00Z",
+              "schema_version": "1.4.0",
+              "id": "RUSTSEC-2023-0018",
               "aliases": [
                 "GHSA-mc8h-8q98-g5hr"
               ],
@@ -1173,6 +1191,10 @@
               "ids": [
                 "GHSA-mc8h-8q98-g5hr",
                 "RUSTSEC-2023-0018"
+              ],
+              "aliases": [
+                "GHSA-mc8h-8q98-g5hr",
+                "RUSTSEC-2023-0018"
               ]
             }
           ]
@@ -1185,10 +1207,10 @@
           },
           "vulnerabilities": [
             {
-              "schema_version": "1.4.0",
-              "id": "GHSA-wcg3-cvx6-7396",
               "modified": "2022-12-06T00:16:25Z",
               "published": "2021-08-25T20:56:46Z",
+              "schema_version": "1.4.0",
+              "id": "GHSA-wcg3-cvx6-7396",
               "aliases": [
                 "CVE-2020-26235"
               ],
@@ -1287,10 +1309,10 @@
               }
             },
             {
-              "schema_version": "1.4.0",
-              "id": "RUSTSEC-2020-0071",
               "modified": "2023-02-08T15:06:38Z",
               "published": "2020-11-18T12:00:00Z",
+              "schema_version": "1.4.0",
+              "id": "RUSTSEC-2020-0071",
               "aliases": [
                 "CVE-2020-26235"
               ],
@@ -1417,11 +1439,24 @@
               "ids": [
                 "GHSA-wcg3-cvx6-7396",
                 "RUSTSEC-2020-0071"
+              ],
+              "aliases": [
+                "GHSA-wcg3-cvx6-7396",
+                "RUSTSEC-2020-0071"
               ]
             }
           ]
         }
       ]
     }
-  ]
+  ],
+  "experimental_config": {
+    "call_analysis": {
+      "enabled": false
+    },
+    "licenses": {
+      "enabled": false,
+      "allowlist": null
+    }
+  }
 }
diff --git a/pkg/osvscanner/fixtures/filter/some/input.json b/pkg/osvscanner/fixtures/filter/some/input.json
index 7a70a02..b7e9fd6 100644
--- a/pkg/osvscanner/fixtures/filter/some/input.json
+++ b/pkg/osvscanner/fixtures/filter/some/input.json
@@ -149,6 +149,10 @@
               "ids": [
                 "GHSA-mc8h-8q98-g5hr",
                 "RUSTSEC-2023-0018"
+              ],
+              "aliases": [
+                "GHSA-mc8h-8q98-g5hr",
+                "RUSTSEC-2023-0018"
               ]
             }
           ]
@@ -393,6 +397,10 @@
               "ids": [
                 "GHSA-wcg3-cvx6-7396",
                 "RUSTSEC-2020-0071"
+              ],
+              "aliases": [
+                "GHSA-wcg3-cvx6-7396",
+                "RUSTSEC-2020-0071"
               ]
             }
           ]
@@ -867,17 +875,28 @@
               "ids": [
                 "GHSA-fxg5-wq6x-vr4w",
                 "GO-2023-1495"
+              ],
+              "aliases": [
+                "GHSA-fxg5-wq6x-vr4w",
+                "GO-2023-1495"
               ]
             },
             {
               "ids": [
                 "GO-2022-1144"
+              ],
+              "aliases": [
+                "GO-2022-1144"
               ]
             },
             {
               "ids": [
                 "GHSA-vvpx-j8f3-3w6h",
                 "GO-2023-1571"
+              ],
+              "aliases": [
+                "GHSA-vvpx-j8f3-3w6h",
+                "GO-2023-1571"
               ]
             }
           ]
@@ -1021,11 +1040,17 @@
             {
               "ids": [
                 "GHSA-mrrw-grhq-86gf"
+              ],
+              "aliases": [
+                "GHSA-mrrw-grhq-86gf"
               ]
             },
             {
               "ids": [
                 "RUSTSEC-2023-0015"
+              ],
+              "aliases": [
+                "RUSTSEC-2023-0015"
               ]
             }
           ]
@@ -1173,6 +1198,10 @@
               "ids": [
                 "GHSA-mc8h-8q98-g5hr",
                 "RUSTSEC-2023-0018"
+              ],
+              "aliases": [
+                "GHSA-mc8h-8q98-g5hr",
+                "RUSTSEC-2023-0018"
               ]
             }
           ]
@@ -1417,6 +1446,10 @@
               "ids": [
                 "GHSA-wcg3-cvx6-7396",
                 "RUSTSEC-2020-0071"
+              ],
+              "aliases": [
+                "GHSA-wcg3-cvx6-7396",
+                "RUSTSEC-2020-0071"
               ]
             }
           ]
diff --git a/pkg/osvscanner/fixtures/filter/some/want.json b/pkg/osvscanner/fixtures/filter/some/want.json
index 6c7bd2c..4b00059 100644
--- a/pkg/osvscanner/fixtures/filter/some/want.json
+++ b/pkg/osvscanner/fixtures/filter/some/want.json
@@ -14,10 +14,10 @@
           },
           "vulnerabilities": [
             {
-              "schema_version": "1.4.0",
-              "id": "GHSA-vvpx-j8f3-3w6h",
               "modified": "2023-03-09T21:20:44Z",
               "published": "2023-02-17T14:00:02Z",
+              "schema_version": "1.4.0",
+              "id": "GHSA-vvpx-j8f3-3w6h",
               "aliases": [
                 "CVE-2022-41723"
               ],
@@ -89,15 +89,14 @@
               }
             },
             {
-              "schema_version": "1.4.0",
-              "id": "GO-2023-1571",
               "modified": "2023-02-22T20:13:12Z",
               "published": "2023-02-16T22:31:36Z",
+              "schema_version": "1.4.0",
+              "id": "GO-2023-1571",
               "aliases": [
                 "CVE-2022-41723",
                 "GHSA-vvpx-j8f3-3w6h"
               ],
-              "summary": "",
               "details": "A maliciously crafted HTTP/2 stream could cause excessive CPU consumption in the HPACK decoder, sufficient to cause a denial of service from a small number of small requests.",
               "affected": [
                 {
@@ -203,6 +202,10 @@
               "ids": [
                 "GHSA-vvpx-j8f3-3w6h",
                 "GO-2023-1571"
+              ],
+              "aliases": [
+                "GHSA-vvpx-j8f3-3w6h",
+                "GO-2023-1571"
               ]
             }
           ]
@@ -223,11 +226,10 @@
           },
           "vulnerabilities": [
             {
-              "schema_version": "1.4.0",
-              "id": "GHSA-mrrw-grhq-86gf",
               "modified": "2023-02-28T20:30:10Z",
               "published": "2023-02-28T20:30:10Z",
-              "aliases": null,
+              "schema_version": "1.4.0",
+              "id": "GHSA-mrrw-grhq-86gf",
               "summary": "Ascii (crate) allows out-of-bounds array indexing in safe code",
               "details": "Affected version of this crate had implementation of `From\u003c\u0026mut AsciiStr\u003e` for `\u0026mut [u8]` and `\u0026mut str`. This can result in out-of-bounds array indexing in safe code.\n\nThe flaw was corrected in commit [8a6c779](https://github.com/tomprogrammer/rust-ascii/pull/63/commits/8a6c7798c202766bd57d70fb8d12739dd68fb9dc) by removing those impls.\n",
               "affected": [
@@ -286,6 +288,9 @@
             {
               "ids": [
                 "GHSA-mrrw-grhq-86gf"
+              ],
+              "aliases": [
+                "GHSA-mrrw-grhq-86gf"
               ]
             }
           ]
@@ -298,10 +303,10 @@
           },
           "vulnerabilities": [
             {
-              "schema_version": "1.4.0",
-              "id": "GHSA-wcg3-cvx6-7396",
               "modified": "2022-12-06T00:16:25Z",
               "published": "2021-08-25T20:56:46Z",
+              "schema_version": "1.4.0",
+              "id": "GHSA-wcg3-cvx6-7396",
               "aliases": [
                 "CVE-2020-26235"
               ],
@@ -400,10 +405,10 @@
               }
             },
             {
-              "schema_version": "1.4.0",
-              "id": "RUSTSEC-2020-0071",
               "modified": "2023-02-08T15:06:38Z",
               "published": "2020-11-18T12:00:00Z",
+              "schema_version": "1.4.0",
+              "id": "RUSTSEC-2020-0071",
               "aliases": [
                 "CVE-2020-26235"
               ],
@@ -530,11 +535,24 @@
               "ids": [
                 "GHSA-wcg3-cvx6-7396",
                 "RUSTSEC-2020-0071"
+              ],
+              "aliases": [
+                "GHSA-wcg3-cvx6-7396",
+                "RUSTSEC-2020-0071"
               ]
             }
           ]
         }
       ]
     }
-  ]
+  ],
+  "experimental_config": {
+    "call_analysis": {
+      "enabled": false
+    },
+    "licenses": {
+      "enabled": false,
+      "allowlist": null
+    }
+  }
 }
diff --git a/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go
index acda849..03a7aed 100644
--- a/pkg/osvscanner/osvscanner.go
+++ b/pkg/osvscanner/osvscanner.go
@@ -39,16 +39,16 @@ type ScannerActions struct {
 	NoIgnore             bool
 	DockerContainerNames []string
 	ConfigOverridePath   string
+	CallAnalysisStates   map[string]bool
 
 	ExperimentalScannerActions
 }
 
 type ExperimentalScannerActions struct {
-	CallAnalysis          bool
 	CompareLocally        bool
 	CompareOffline        bool
 	ShowAllPackages       bool
-	ScanLicenses          bool
+	ScanLicensesSummary   bool
 	ScanLicensesAllowlist []string
 
 	LocalDBPath string
@@ -635,20 +635,20 @@ func filterPackageVulns(r reporter.Reporter, pkgVulns models.PackageVulns, confi
 	var newGroups []models.GroupInfo
 	for _, group := range pkgVulns.Groups {
 		ignore := false
-		for _, id := range group.IDs {
+		for _, id := range group.Aliases {
 			var ignoreLine config.IgnoreEntry
 			if ignore, ignoreLine = configToUse.ShouldIgnore(id); ignore {
-				for _, id := range group.IDs {
+				for _, id := range group.Aliases {
 					ignoredVulns[id] = struct{}{}
 				}
 				// NB: This only prints the first reason encountered in all the aliases.
-				switch len(group.IDs) {
+				switch len(group.Aliases) {
 				case 1:
 					r.PrintText(fmt.Sprintf("%s has been filtered out because: %s\n", ignoreLine.ID, ignoreLine.Reason))
 				case 2:
 					r.PrintText(fmt.Sprintf("%s and 1 alias have been filtered out because: %s\n", ignoreLine.ID, ignoreLine.Reason))
 				default:
-					r.PrintText(fmt.Sprintf("%s and %d aliases have been filtered out because: %s\n", ignoreLine.ID, len(group.IDs)-1, ignoreLine.Reason))
+					r.PrintText(fmt.Sprintf("%s and %d aliases have been filtered out because: %s\n", ignoreLine.ID, len(group.Aliases)-1, ignoreLine.Reason))
 				}
 
 				break
@@ -786,13 +786,13 @@ func DoScan(actions ScannerActions, r reporter.Reporter) (models.VulnerabilityRe
 	}
 
 	var licensesResp [][]models.License
-	if actions.ScanLicenses {
+	if len(actions.ScanLicensesAllowlist) > 0 || actions.ScanLicensesSummary {
 		licensesResp, err = makeLicensesRequests(filteredScannedPackages)
 		if err != nil {
 			return models.VulnerabilityResults{}, err
 		}
 	}
-	results := buildVulnerabilityResults(r, filteredScannedPackages, vulnsResp, licensesResp, actions.CallAnalysis, actions.ShowAllPackages, actions.ScanLicenses, actions.ScanLicensesAllowlist)
+	results := buildVulnerabilityResults(r, filteredScannedPackages, vulnsResp, licensesResp, actions)
 
 	filtered := filterResults(r, &results, &configManager, actions.ShowAllPackages)
 	if filtered > 0 {
@@ -822,6 +822,7 @@ func DoScan(actions ScannerActions, r reporter.Reporter) (models.VulnerabilityRe
 			}
 		}
 		onlyUncalledVuln = onlyUncalledVuln && vuln
+		licenseViolation = licenseViolation && len(actions.ScanLicensesAllowlist) > 0
 
 		if (!vuln || onlyUncalledVuln) && !licenseViolation {
 			// There is no error.
diff --git a/pkg/osvscanner/vulnerability_result.go b/pkg/osvscanner/vulnerability_result.go
index cbdb104..2d20e4e 100644
--- a/pkg/osvscanner/vulnerability_result.go
+++ b/pkg/osvscanner/vulnerability_result.go
@@ -21,21 +21,14 @@ func buildVulnerabilityResults(
 	packages []scannedPackage,
 	vulnsResp *osv.HydratedBatchedResponse,
 	licensesResp [][]models.License,
-	callAnalysis bool,
-	allPackages bool,
-	licenses bool,
-	licensesAllowlist []string,
+	actions ScannerActions,
 ) models.VulnerabilityResults {
 	output := models.VulnerabilityResults{
 		Results: []models.PackageSource{},
 	}
 	groupedBySource := map[models.SourceInfo][]models.PackageVulns{}
-	if len(licensesAllowlist) == 1 && licensesAllowlist[0] == "" {
-		licensesAllowlist = []string{}
-	}
-
 	for i, rawPkg := range packages {
-		includePackage := allPackages
+		includePackage := actions.ShowAllPackages
 		var pkg models.PackageVulns
 		if rawPkg.Commit != "" {
 			pkg.Package.Commit = rawPkg.Commit
@@ -63,10 +56,10 @@ func buildVulnerabilityResults(
 			pkg.Vulnerabilities = vulnsResp.Results[i].Vulns
 			pkg.Groups = grouper.Group(grouper.ConvertVulnerabilityToIDAliases(pkg.Vulnerabilities))
 		}
-		if licenses {
+		if len(actions.ScanLicensesAllowlist) > 0 {
 			pkg.Licenses = licensesResp[i]
 			allowlist := make(map[string]bool)
-			for _, license := range licensesAllowlist {
+			for _, license := range actions.ScanLicensesAllowlist {
 				allowlist[strings.ToLower(license)] = true
 			}
 			for _, license := range pkg.Licenses {
@@ -78,15 +71,16 @@ func buildVulnerabilityResults(
 				includePackage = true
 			}
 		}
+		if actions.ScanLicensesSummary {
+			pkg.Licenses = licensesResp[i]
+		}
 		if includePackage {
 			groupedBySource[rawPkg.Source] = append(groupedBySource[rawPkg.Source], pkg)
 		}
 	}
 
 	for source, packages := range groupedBySource {
-		if callAnalysis {
-			sourceanalysis.Run(r, source, packages)
-		}
+		sourceanalysis.Run(r, source, packages, actions.CallAnalysisStates)
 		output.Results = append(output.Results, models.PackageSource{
 			Source:   source,
 			Packages: packages,
@@ -101,17 +95,14 @@ func buildVulnerabilityResults(
 		return output.Results[i].Source.Path < output.Results[j].Source.Path
 	})
 
-	if licenses {
-		output.ExperimentalAnalysisConfig.Licenses.Enabled = true
-		allowlist := make([]models.License, len(licensesAllowlist))
-		for i, l := range licensesAllowlist {
+	if len(actions.ScanLicensesAllowlist) > 0 || actions.ScanLicensesSummary {
+		output.ExperimentalAnalysisConfig.Licenses.Summary = actions.ScanLicensesSummary
+		allowlist := make([]models.License, len(actions.ScanLicensesAllowlist))
+		for i, l := range actions.ScanLicensesAllowlist {
 			allowlist[i] = models.License(l)
 		}
 		output.ExperimentalAnalysisConfig.Licenses.Allowlist = allowlist
 	}
-	if callAnalysis {
-		output.ExperimentalAnalysisConfig.CallAnalysis.Enabled = true
-	}
 
 	return output
 }


Test patch:
diff --git a/cmd/osv-scanner/callanalysis_parser_test.go b/cmd/osv-scanner/callanalysis_parser_test.go
new file mode 100644
index 0000000..db8c0bc
--- /dev/null
+++ b/cmd/osv-scanner/callanalysis_parser_test.go
@@ -0,0 +1,64 @@
+package main
+
+import (
+	"reflect"
+	"testing"
+)
+
+func TestCreateCallAnalysisStates(t *testing.T) {
+	t.Parallel()
+	testCases := []struct {
+		enabledCallAnalysis        []string
+		disabledCallAnalysis       []string
+		expectedCallAnalysisStates map[string]bool
+	}{
+		{
+			enabledCallAnalysis:  []string{"go", "rust"},
+			disabledCallAnalysis: []string{},
+			expectedCallAnalysisStates: map[string]bool{
+				"go":   true,
+				"rust": true,
+			},
+		},
+		{
+			enabledCallAnalysis:  []string{"all"},
+			disabledCallAnalysis: []string{"rust"},
+			expectedCallAnalysisStates: map[string]bool{
+				"go":   true,
+				"rust": false,
+			},
+		},
+		{
+			enabledCallAnalysis:  []string{},
+			disabledCallAnalysis: []string{"all"},
+			expectedCallAnalysisStates: map[string]bool{
+				"go":   false,
+				"rust": false,
+			},
+		},
+		{
+			enabledCallAnalysis:  []string{},
+			disabledCallAnalysis: []string{"rust"},
+			expectedCallAnalysisStates: map[string]bool{
+				"go":   true,
+				"rust": false,
+			},
+		},
+		{
+			enabledCallAnalysis:  []string{"all", "rust"},
+			disabledCallAnalysis: []string{"go"},
+			expectedCallAnalysisStates: map[string]bool{
+				"go":   false,
+				"rust": true,
+			},
+		},
+	}
+
+	for _, testCase := range testCases {
+		actualCallAnalysisStates := createCallAnalysisStates(testCase.enabledCallAnalysis, testCase.disabledCallAnalysis)
+
+		if !reflect.DeepEqual(actualCallAnalysisStates, testCase.expectedCallAnalysisStates) {
+			t.Errorf("expected call analysis states to be %v, but got %v", testCase.expectedCallAnalysisStates, actualCallAnalysisStates)
+		}
+	}
+}
diff --git a/cmd/osv-scanner/fixtures/locks-test-ignore/osv-scanner.toml b/cmd/osv-scanner/fixtures/locks-test-ignore/osv-scanner.toml
new file mode 100644
index 0000000..e05bc8f
--- /dev/null
+++ b/cmd/osv-scanner/fixtures/locks-test-ignore/osv-scanner.toml
@@ -0,0 +1,3 @@
+[[IgnoredVulns]]
+id = "CVE-2021-23424"
+reason = "Test manifest file (alpine.cdx.xml)"
diff --git a/cmd/osv-scanner/fixtures/locks-test-ignore/package-lock.json b/cmd/osv-scanner/fixtures/locks-test-ignore/package-lock.json
new file mode 100644
index 0000000..e3a2d44
--- /dev/null
+++ b/cmd/osv-scanner/fixtures/locks-test-ignore/package-lock.json
@@ -0,0 +1,9 @@
+{
+  "requires": true,
+  "lockfileVersion": 1,
+  "dependencies": {
+    "ansi-html": {
+      "version": "0.0.1"
+    }
+  }
+}
diff --git a/cmd/osv-scanner/main_test.go b/cmd/osv-scanner/main_test.go
index 79f1c25..05afb81 100644
--- a/cmd/osv-scanner/main_test.go
+++ b/cmd/osv-scanner/main_test.go
@@ -266,7 +266,7 @@ func TestRun(t *testing.T) {
 				Scanned <rootdir>/fixtures/locks-many/yarn.lock file and found 1 package
 				Loaded filter from: <rootdir>/fixtures/locks-many/osv-scanner.toml
 				CVE-2022-48174 has been filtered out because: Test manifest file (alpine.cdx.xml)
-				GHSA-whgm-jr23-g3j9 has been filtered out because: Test manifest file
+				GHSA-whgm-jr23-g3j9 and 1 alias have been filtered out because: Test manifest file
 				Filtered 2 vulnerabilities from output
 				No issues found
 			`,
@@ -274,7 +274,7 @@ func TestRun(t *testing.T) {
 		},
 		// all supported lockfiles in the directory should be checked
 		{
-			name:         "",
+			name:         "all supported lockfiles in the directory should be checked",
 			args:         []string{"", "./fixtures/locks-many-with-invalid"},
 			wantExitCode: 127,
 			wantStdout: `
@@ -288,7 +288,7 @@ func TestRun(t *testing.T) {
 		},
 		// only the files in the given directories are checked by default (no recursion)
 		{
-			name:         "",
+			name:         "only the files in the given directories are checked by default (no recursion)",
 			args:         []string{"", "./fixtures/locks-one-with-nested"},
 			wantExitCode: 0,
 			wantStdout: `
@@ -300,7 +300,7 @@ func TestRun(t *testing.T) {
 		},
 		// nested directories are checked when `--recursive` is passed
 		{
-			name:         "",
+			name:         "nested directories are checked when `--recursive` is passed",
 			args:         []string{"", "--recursive", "./fixtures/locks-one-with-nested"},
 			wantExitCode: 0,
 			wantStdout: `
@@ -345,18 +345,15 @@ func TestRun(t *testing.T) {
 		},
 		// output with json
 		{
-			name:         "",
+			name:         "json output 1",
 			args:         []string{"", "--json", "./fixtures/locks-many/composer.lock"},
 			wantExitCode: 0,
 			wantStdout: `
 				{
 					"results": [],
 					"experimental_config": {
-						"call_analysis": {
-							"enabled": false
-						},
 						"licenses": {
-							"enabled": false,
+							"summary": false,
 							"allowlist": null
 						}
 					}
@@ -368,18 +365,15 @@ func TestRun(t *testing.T) {
 			`,
 		},
 		{
-			name:         "",
+			name:         "json output 2",
 			args:         []string{"", "--format", "json", "./fixtures/locks-many/composer.lock"},
 			wantExitCode: 0,
 			wantStdout: `
 				{
 					"results": [],
 					"experimental_config": {
-						"call_analysis": {
-							"enabled": false
-						},
 						"licenses": {
-							"enabled": false,
+							"summary": false,
 							"allowlist": null
 						}
 					}
@@ -539,6 +533,21 @@ func TestRun(t *testing.T) {
 				unsupported output format "unknown" - must be one of: table, json, markdown, sarif, gh-annotations
 			`,
 		},
+		// one specific supported lockfile with ignore
+		{
+			name:         "one specific supported lockfile with ignore",
+			args:         []string{"", "./fixtures/locks-test-ignore/package-lock.json"},
+			wantExitCode: 0,
+			wantStdout: `
+				Scanning dir ./fixtures/locks-test-ignore/package-lock.json
+				Scanned <rootdir>/fixtures/locks-test-ignore/package-lock.json file and found 1 package
+				Loaded filter from: <rootdir>/fixtures/locks-test-ignore/osv-scanner.toml
+				CVE-2021-23424 and 1 alias have been filtered out because: Test manifest file (alpine.cdx.xml)
+				Filtered 1 vulnerability from output
+				No issues found
+			`,
+			wantStderr: "",
+		},
 	}
 	for _, tt := range tests {
 		tt := tt
@@ -969,7 +978,7 @@ func TestRun_LocalDatabases(t *testing.T) {
 				Loaded Packagist local db from %%/osv-scanner/Packagist/all.zip
 				Loaded npm local db from %%/osv-scanner/npm/all.zip
 				Loaded filter from: <rootdir>/fixtures/locks-many/osv-scanner.toml
-				GHSA-whgm-jr23-g3j9 has been filtered out because: Test manifest file
+				GHSA-whgm-jr23-g3j9 and 1 alias have been filtered out because: Test manifest file
 				Filtered 1 vulnerability from output
 				No issues found
 			`,
@@ -1065,11 +1074,8 @@ func TestRun_LocalDatabases(t *testing.T) {
 				{
 					"results": [],
 					"experimental_config": {
-						"call_analysis": {
-							"enabled": false
-						},
 						"licenses": {
-							"enabled": false,
+							"summary": false,
 							"allowlist": null
 						}
 					}
@@ -1089,11 +1095,8 @@ func TestRun_LocalDatabases(t *testing.T) {
 				{
 					"results": [],
 					"experimental_config": {
-						"call_analysis": {
-							"enabled": false
-						},
 						"licenses": {
-							"enabled": false,
+							"summary": false,
 							"allowlist": null
 						}
 					}
@@ -1144,9 +1147,9 @@ func TestRun_Licenses(t *testing.T) {
 	t.Parallel()
 	tests := []cliTestCase{
 		{
-			name:         "No vulnerabilities but contains license violations",
-			args:         []string{"", "--experimental-licenses", "", "./fixtures/locks-many"},
-			wantExitCode: 1,
+			name:         "No vulnerabilities with license summary",
+			args:         []string{"", "--experimental-licenses-summary", "./fixtures/locks-many"},
+			wantExitCode: 0,
 			wantStdout: `
 				Scanning dir ./fixtures/locks-many
 				Scanned <rootdir>/fixtures/locks-many/Gemfile.lock file and found 1 package
@@ -1156,7 +1159,7 @@ func TestRun_Licenses(t *testing.T) {
 				Scanned <rootdir>/fixtures/locks-many/yarn.lock file and found 1 package
 				Loaded filter from: <rootdir>/fixtures/locks-many/osv-scanner.toml
 				CVE-2022-48174 has been filtered out because: Test manifest file (alpine.cdx.xml)
-				GHSA-whgm-jr23-g3j9 has been filtered out because: Test manifest file
+				GHSA-whgm-jr23-g3j9 and 1 alias have been filtered out because: Test manifest file
 				Filtered 2 vulnerabilities from output
 				+------------+-------------------------+
 				| LICENSE    | NO. OF PACKAGE VERSIONS |
@@ -1169,9 +1172,9 @@ func TestRun_Licenses(t *testing.T) {
 			wantStderr: "",
 		},
 		{
-			name:         "No vulnerabilities but contains license violations markdown",
-			args:         []string{"", "--experimental-licenses", "", "--format=markdown", "./fixtures/locks-many"},
-			wantExitCode: 1,
+			name:         "No vulnerabilities with license summary in markdown",
+			args:         []string{"", "--experimental-licenses-summary", "--format=markdown", "./fixtures/locks-many"},
+			wantExitCode: 0,
 			wantStdout: `Scanning dir ./fixtures/locks-many
 Scanned <rootdir>/fixtures/locks-many/Gemfile.lock file and found 1 package
 Scanned <rootdir>/fixtures/locks-many/alpine.cdx.xml as CycloneDX SBOM and found 15 packages
@@ -1180,7 +1183,7 @@ Scanned <rootdir>/fixtures/locks-many/package-lock.json file and found 1 package
 Scanned <rootdir>/fixtures/locks-many/yarn.lock file and found 1 package
 Loaded filter from: <rootdir>/fixtures/locks-many/osv-scanner.toml
 CVE-2022-48174 has been filtered out because: Test manifest file (alpine.cdx.xml)
-GHSA-whgm-jr23-g3j9 has been filtered out because: Test manifest file
+GHSA-whgm-jr23-g3j9 and 1 alias have been filtered out because: Test manifest file
 Filtered 2 vulnerabilities from output
 | License | No. of package versions |
 | --- | ---:|
@@ -1191,8 +1194,8 @@ Filtered 2 vulnerabilities from output
 			wantStderr: "",
 		},
 		{
-			name:         "Vulnerabilities and license violations",
-			args:         []string{"", "--experimental-licenses", "", "--config=./fixtures/osv-scanner-empty-config.toml", "./fixtures/locks-many/package-lock.json"},
+			name:         "Vulnerabilities and license summary",
+			args:         []string{"", "--experimental-licenses-summary", "--config=./fixtures/osv-scanner-empty-config.toml", "./fixtures/locks-many/package-lock.json"},
 			wantExitCode: 1,
 			wantStdout: `
 				Scanning dir ./fixtures/locks-many/package-lock.json
@@ -1262,8 +1265,7 @@ Filtered 2 vulnerabilities from output
 								"package": {
 									"name": "babel",
 									"version": "6.23.0",
-									"ecosystem": "npm",
-									"commit": ""
+									"ecosystem": "npm"
 								},
 								"licenses": [
 									"MIT"
@@ -1273,8 +1275,7 @@ Filtered 2 vulnerabilities from output
 								"package": {
 									"name": "human-signals",
 									"version": "5.0.0",
-									"ecosystem": "npm",
-									"commit": ""
+									"ecosystem": "npm"
 								},
 								"licenses": [
 									"Apache-2.0"
@@ -1287,8 +1288,7 @@ Filtered 2 vulnerabilities from output
 								"package": {
 									"name": "ms",
 									"version": "2.1.3",
-									"ecosystem": "npm",
-									"commit": ""
+									"ecosystem": "npm"
 								},
 								"licenses": [
 									"MIT"
@@ -1298,11 +1298,8 @@ Filtered 2 vulnerabilities from output
 					}
 				],
 				"experimental_config": {
-					"call_analysis": {
-						"enabled": false
-					},
 					"licenses": {
-						"enabled": true,
+						"summary": false,
 						"allowlist": [
 							"MIT"
 						]
@@ -1332,8 +1329,7 @@ Filtered 2 vulnerabilities from output
 								"package": {
 									"name": "human-signals",
 									"version": "5.0.0",
-									"ecosystem": "npm",
-									"commit": ""
+									"ecosystem": "npm"
 								},
 								"licenses": [
 									"Apache-2.0"
@@ -1346,11 +1342,8 @@ Filtered 2 vulnerabilities from output
 					}
 				],
 				"experimental_config": {
-					"call_analysis": {
-						"enabled": false
-					},
 					"licenses": {
-						"enabled": true,
+						"summary": false,
 						"allowlist": [
 							"MIT"
 						]
@@ -1380,8 +1373,7 @@ Filtered 2 vulnerabilities from output
 								"package": {
 									"name": "babel",
 									"version": "6.23.0",
-									"ecosystem": "npm",
-									"commit": ""
+									"ecosystem": "npm"
 								},
 								"licenses": [
 									"MIT"
@@ -1391,8 +1383,7 @@ Filtered 2 vulnerabilities from output
 								"package": {
 									"name": "human-signals",
 									"version": "5.0.0",
-									"ecosystem": "npm",
-									"commit": ""
+									"ecosystem": "npm"
 								},
 								"licenses": [
 									"Apache-2.0"
@@ -1402,8 +1393,7 @@ Filtered 2 vulnerabilities from output
 								"package": {
 									"name": "ms",
 									"version": "2.1.3",
-									"ecosystem": "npm",
-									"commit": ""
+									"ecosystem": "npm"
 								},
 								"licenses": [
 									"MIT"
@@ -1413,11 +1403,8 @@ Filtered 2 vulnerabilities from output
 					}
 				],
 				"experimental_config": {
-					"call_analysis": {
-						"enabled": false
-					},
 					"licenses": {
-						"enabled": true,
+						"summary": false,
 						"allowlist": [
 							"MIT",
 							"Apache-2.0"
@@ -1431,6 +1418,65 @@ Filtered 2 vulnerabilities from output
 			Scanned <rootdir>/fixtures/locks-licenses/package-lock.json file and found 3 packages
 			`,
 		},
+		{
+			name:         "Licenses in summary mode json",
+			args:         []string{"", "--format=json", "--experimental-licenses-summary", "./fixtures/locks-licenses/package-lock.json"},
+			wantExitCode: 0,
+			wantStdout: `
+			{
+				"results": [
+					{
+						"source": {
+							"path": "<rootdir>/fixtures/locks-licenses/package-lock.json",
+							"type": "lockfile"
+						},
+						"packages": [
+							{
+								"package": {
+									"name": "babel",
+									"version": "6.23.0",
+									"ecosystem": "npm"
+								},
+								"licenses": [
+									"MIT"
+								]
+							},
+							{
+								"package": {
+									"name": "human-signals",
+									"version": "5.0.0",
+									"ecosystem": "npm"
+								},
+								"licenses": [
+									"Apache-2.0"
+								]
+							},
+							{
+								"package": {
+									"name": "ms",
+									"version": "2.1.3",
+									"ecosystem": "npm"
+								},
+								"licenses": [
+									"MIT"
+								]
+							}
+						]
+					}
+				],
+				"experimental_config": {
+					"licenses": {
+						"summary": true,
+						"allowlist": []
+					}
+				}
+			}
+		`,
+			wantStderr: `
+			Scanning dir ./fixtures/locks-licenses/package-lock.json
+			Scanned <rootdir>/fixtures/locks-licenses/package-lock.json file and found 3 packages
+			`,
+		},
 	}
 	for _, tt := range tests {
 		tt := tt
diff --git a/internal/ci/fixtures/vulns/test-vuln-diff-a-a-1.json b/internal/ci/fixtures/vulns/test-vuln-diff-a-a-1.json
index 668cedf..8fd83e0 100644
--- a/internal/ci/fixtures/vulns/test-vuln-diff-a-a-1.json
+++ b/internal/ci/fixtures/vulns/test-vuln-diff-a-a-1.json
@@ -75,11 +75,21 @@
             {
               "ids": [
                 "GO-2021-0053"
-              ]
+              ],
+              "aliases": null
             }
           ]
         }
       ]
     }
-  ]
-}
\ No newline at end of file
+  ],
+  "experimental_config": {
+    "call_analysis": {
+      "enabled": false
+    },
+    "licenses": {
+      "enabled": false,
+      "allowlist": null
+    }
+  }
+}
diff --git a/internal/ci/fixtures/vulns/test-vuln-diff-a-a.json b/internal/ci/fixtures/vulns/test-vuln-diff-a-a.json
index 8932c42..786594f 100644
--- a/internal/ci/fixtures/vulns/test-vuln-diff-a-a.json
+++ b/internal/ci/fixtures/vulns/test-vuln-diff-a-a.json
@@ -1,3 +1,12 @@
 {
-  "results": []
+  "results": [],
+  "experimental_config": {
+    "call_analysis": {
+      "enabled": false
+    },
+    "licenses": {
+      "enabled": false,
+      "allowlist": null
+    }
+  }
 }
diff --git a/internal/ci/fixtures/vulns/test-vuln-diff-a-b.json b/internal/ci/fixtures/vulns/test-vuln-diff-a-b.json
index 06324f9..8b29d0a 100644
--- a/internal/ci/fixtures/vulns/test-vuln-diff-a-b.json
+++ b/internal/ci/fixtures/vulns/test-vuln-diff-a-b.json
@@ -112,11 +112,24 @@
             {
               "ids": [
                 "GHSA-c3h9-896r-86jm"
+              ],
+              "aliases": [
+                "CVE-2021-3121",
+                "GHSA-c3h9-896r-86jm"
               ]
             }
           ]
         }
       ]
     }
-  ]
-}
\ No newline at end of file
+  ],
+  "experimental_config": {
+    "call_analysis": {
+      "enabled": false
+    },
+    "licenses": {
+      "enabled": false,
+      "allowlist": null
+    }
+  }
+}
diff --git a/internal/ci/fixtures/vulns/test-vuln-diff-b-a.json b/internal/ci/fixtures/vulns/test-vuln-diff-b-a.json
index 8932c42..786594f 100644
--- a/internal/ci/fixtures/vulns/test-vuln-diff-b-a.json
+++ b/internal/ci/fixtures/vulns/test-vuln-diff-b-a.json
@@ -1,3 +1,12 @@
 {
-  "results": []
+  "results": [],
+  "experimental_config": {
+    "call_analysis": {
+      "enabled": false
+    },
+    "licenses": {
+      "enabled": false,
+      "allowlist": null
+    }
+  }
 }
diff --git a/internal/ci/fixtures/vulns/test-vuln-diff-b-c.json b/internal/ci/fixtures/vulns/test-vuln-diff-b-c.json
index 8932c42..786594f 100644
--- a/internal/ci/fixtures/vulns/test-vuln-diff-b-c.json
+++ b/internal/ci/fixtures/vulns/test-vuln-diff-b-c.json
@@ -1,3 +1,12 @@
 {
-  "results": []
+  "results": [],
+  "experimental_config": {
+    "call_analysis": {
+      "enabled": false
+    },
+    "licenses": {
+      "enabled": false,
+      "allowlist": null
+    }
+  }
 }
diff --git a/internal/ci/fixtures/vulns/test-vuln-diff-c-b.json b/internal/ci/fixtures/vulns/test-vuln-diff-c-b.json
index a8ef63e..76d77a5 100644
--- a/internal/ci/fixtures/vulns/test-vuln-diff-c-b.json
+++ b/internal/ci/fixtures/vulns/test-vuln-diff-c-b.json
@@ -112,6 +112,10 @@
             {
               "ids": [
                 "GHSA-c3h9-896r-86jm"
+              ],
+              "aliases": [
+                "CVE-2021-3121",
+                "GHSA-c3h9-896r-86jm"
               ]
             }
           ]
@@ -319,11 +323,21 @@
               "ids": [
                 "GHSA-m5pq-gvj9-9vr8",
                 "RUSTSEC-2022-0013"
-              ]
+              ],
+              "aliases": null
             }
           ]
         }
       ]
     }
-  ]
-}
\ No newline at end of file
+  ],
+  "experimental_config": {
+    "call_analysis": {
+      "enabled": false
+    },
+    "licenses": {
+      "enabled": false,
+      "allowlist": null
+    }
+  }
+}
diff --git a/internal/testutility/utility.go b/internal/testutility/utility.go
index 361581a..3817a39 100644
--- a/internal/testutility/utility.go
+++ b/internal/testutility/utility.go
@@ -82,6 +82,9 @@ func AssertMatchFixtureJSON[V any](t *testing.T, path string, val V) {
 	}
 }
 
+// Deprecated: Not actually deprecated, deprecated tag used to warn users from checking in as
+// it's meant to be used as a helper func to generate fixtures
+//
 // CreateJSONFixture creates a JSON file at path of the value val,
 // can be used with AssertMatchFixtureJSON to compare against future values.
 func CreateJSONFixture[V any](t *testing.T, path string, val V) {
diff --git a/pkg/grouper/grouper_test.go b/pkg/grouper/grouper_test.go
index be06f14..32cf2c9 100644
--- a/pkg/grouper/grouper_test.go
+++ b/pkg/grouper/grouper_test.go
@@ -81,16 +81,20 @@ func TestGroup(t *testing.T) {
 			},
 			want: []models.GroupInfo{
 				{
-					IDs: []string{v1.ID, v2.ID, v3.ID},
+					IDs:     []string{v1.ID, v2.ID, v3.ID},
+					Aliases: []string{v1.ID, v2.ID, v3.ID},
 				},
 				{
-					IDs: []string{v4.ID, v5.ID, v6.ID},
+					IDs:     []string{v4.ID, v5.ID, v6.ID},
+					Aliases: []string{v4.ID, v5.ID, v6.ID, v4.Aliases[0], v4.Aliases[1], v5.Aliases[1]},
 				},
 				{
-					IDs: []string{v7.ID},
+					IDs:     []string{v7.ID},
+					Aliases: []string{v7.Aliases[0], v7.ID},
 				},
 				{
-					IDs: []string{v8.ID},
+					IDs:     []string{v8.ID},
+					Aliases: []string{v8.Aliases[0], v8.ID},
 				},
 			},
 		},
@@ -100,22 +104,28 @@ func TestGroup(t *testing.T) {
 			},
 			want: []models.GroupInfo{
 				{
-					IDs: []string{v8.ID},
+					IDs:     []string{v8.ID},
+					Aliases: []string{v8.Aliases[0], v8.ID},
 				},
 				{
-					IDs: []string{v1.ID, v2.ID, v3.ID}, // Deterministic order
+					IDs:     []string{v1.ID, v2.ID, v3.ID}, // Deterministic order
+					Aliases: []string{v1.ID, v2.ID, v3.ID}, // Deterministic order
 				},
 				{
-					IDs: []string{v4.ID, v5.ID, v6.ID},
+					IDs:     []string{v4.ID, v5.ID, v6.ID},
+					Aliases: []string{v4.ID, v5.ID, v6.ID, v4.Aliases[0], v4.Aliases[1], v5.Aliases[1]},
 				},
 				{
-					IDs: []string{v7.ID},
+					IDs:     []string{v7.ID},
+					Aliases: []string{v7.Aliases[0], v7.ID},
 				},
 				{
-					IDs: []string{v9.ID},
+					IDs:     []string{v9.ID},
+					Aliases: []string{v9.ID},
 				},
 				{
-					IDs: []string{v10.ID},
+					IDs:     []string{v10.ID},
+					Aliases: []string{v10.ID},
 				},
 			},
 		},
@@ -125,10 +135,12 @@ func TestGroup(t *testing.T) {
 			},
 			want: []models.GroupInfo{
 				{
-					IDs: []string{v9.ID},
+					IDs:     []string{v9.ID},
+					Aliases: []string{v9.ID},
 				},
 				{
-					IDs: []string{v10.ID},
+					IDs:     []string{v10.ID},
+					Aliases: []string{v10.ID},
 				},
 			},
 		},
diff --git a/pkg/osvscanner/osvscanner_internal_test.go b/pkg/osvscanner/osvscanner_internal_test.go
index f5b1340..2986aff 100644
--- a/pkg/osvscanner/osvscanner_internal_test.go
+++ b/pkg/osvscanner/osvscanner_internal_test.go
@@ -65,6 +65,7 @@ func Test_filterResults(t *testing.T) {
 				out := filepath.Join(tt.testCase.path, "out.json")
 				t.Errorf("filterResults() returned an unexpected results (-want, +got):\n%s\n"+
 					"Full json output written to %s", diff, out)
+				//nolint:staticcheck
 				testutility.CreateJSONFixture(t, out, got)
 			}
 			if filtered != tt.testCase.numFiltered {
diff --git a/pkg/osvscanner/vulnerability_result_internal_test.go b/pkg/osvscanner/vulnerability_result_internal_test.go
index 5a81b67..5781f4e 100644
--- a/pkg/osvscanner/vulnerability_result_internal_test.go
+++ b/pkg/osvscanner/vulnerability_result_internal_test.go
@@ -13,14 +13,11 @@ import (
 func Test_assembleResult(t *testing.T) {
 	t.Parallel()
 	type args struct {
-		r                 reporter.Reporter
-		packages          []scannedPackage
-		vulnsResp         *osv.HydratedBatchedResponse
-		licensesResp      [][]models.License
-		callAnalysis      bool
-		allPackages       bool
-		licenses          bool
-		licensesAllowlist []string
+		r            reporter.Reporter
+		packages     []scannedPackage
+		vulnsResp    *osv.HydratedBatchedResponse
+		licensesResp [][]models.License
+		actions      ScannerActions
 	}
 	packages := []scannedPackage{
 		{
@@ -75,6 +72,8 @@ func Test_assembleResult(t *testing.T) {
 		{models.License("UNKNOWN")},
 	}
 
+	callAnalysisStates := make(map[string]bool)
+
 	tests := []struct {
 		name string
 		args args
@@ -82,14 +81,17 @@ func Test_assembleResult(t *testing.T) {
 	}{{
 		name: "group vulnerabilities",
 		args: args{
-			r:                 &reporter.VoidReporter{},
-			packages:          packages,
-			vulnsResp:         vulnsResp,
-			licensesResp:      licensesResp,
-			allPackages:       false,
-			callAnalysis:      false,
-			licenses:          false,
-			licensesAllowlist: nil,
+			r:            &reporter.VoidReporter{},
+			packages:     packages,
+			vulnsResp:    vulnsResp,
+			licensesResp: licensesResp,
+			actions: ScannerActions{
+				ExperimentalScannerActions: ExperimentalScannerActions{
+					ShowAllPackages:       false,
+					ScanLicensesAllowlist: nil,
+				},
+				CallAnalysisStates: callAnalysisStates,
+			},
 		},
 		want: models.VulnerabilityResults{
 			Results: []models.PackageSource{
@@ -115,7 +117,10 @@ func Test_assembleResult(t *testing.T) {
 								},
 							},
 							Groups: []models.GroupInfo{
-								{IDs: []string{"CVE-123", "GHSA-123"}},
+								{
+									IDs:     []string{"CVE-123", "GHSA-123"},
+									Aliases: []string{"CVE-123", "GHSA-123"},
+								},
 							},
 						},
 					},
@@ -136,7 +141,10 @@ func Test_assembleResult(t *testing.T) {
 								{ID: "GHSA-456"},
 							},
 							Groups: []models.GroupInfo{
-								{IDs: []string{"GHSA-456"}},
+								{
+									IDs:     []string{"GHSA-456"},
+									Aliases: []string{"GHSA-456"},
+								},
 							},
 						},
 					},
@@ -146,14 +154,17 @@ func Test_assembleResult(t *testing.T) {
 	}, {
 		name: "group vulnerabilities, with all packages included",
 		args: args{
-			r:                 &reporter.VoidReporter{},
-			packages:          packages,
-			vulnsResp:         vulnsResp,
-			licensesResp:      licensesResp,
-			allPackages:       true,
-			callAnalysis:      false,
-			licenses:          false,
-			licensesAllowlist: nil,
+			r:            &reporter.VoidReporter{},
+			packages:     packages,
+			vulnsResp:    vulnsResp,
+			licensesResp: licensesResp,
+			actions: ScannerActions{
+				ExperimentalScannerActions: ExperimentalScannerActions{
+					ShowAllPackages:       true,
+					ScanLicensesAllowlist: nil,
+				},
+				CallAnalysisStates: callAnalysisStates,
+			},
 		},
 		want: models.VulnerabilityResults{
 			Results: []models.PackageSource{
@@ -179,7 +190,10 @@ func Test_assembleResult(t *testing.T) {
 								},
 							},
 							Groups: []models.GroupInfo{
-								{IDs: []string{"CVE-123", "GHSA-123"}},
+								{
+									IDs:     []string{"CVE-123", "GHSA-123"},
+									Aliases: []string{"CVE-123", "GHSA-123"},
+								},
 							},
 						}, {
 							Package: models.PackageInfo{
@@ -206,7 +220,10 @@ func Test_assembleResult(t *testing.T) {
 								{ID: "GHSA-456"},
 							},
 							Groups: []models.GroupInfo{
-								{IDs: []string{"GHSA-456"}},
+								{
+									IDs:     []string{"GHSA-456"},
+									Aliases: []string{"GHSA-456"},
+								},
 							},
 						},
 					},
@@ -216,19 +233,23 @@ func Test_assembleResult(t *testing.T) {
 	}, {
 		name: "group vulnerabilities with licenses",
 		args: args{
-			r:                 &reporter.VoidReporter{},
-			packages:          packages,
-			vulnsResp:         vulnsResp,
-			licensesResp:      licensesResp,
-			allPackages:       false,
-			callAnalysis:      false,
-			licenses:          true,
-			licensesAllowlist: nil,
+			r:            &reporter.VoidReporter{},
+			packages:     packages,
+			vulnsResp:    vulnsResp,
+			licensesResp: licensesResp,
+			actions: ScannerActions{
+				ExperimentalScannerActions: ExperimentalScannerActions{
+					ShowAllPackages:       true,
+					ScanLicensesSummary:   true,
+					ScanLicensesAllowlist: nil,
+				},
+				CallAnalysisStates: callAnalysisStates,
+			},
 		},
 		want: models.VulnerabilityResults{
 			ExperimentalAnalysisConfig: models.ExperimentalAnalysisConfig{
 				Licenses: models.ExperimentalLicenseConfig{
-					Enabled:   true,
+					Summary:   true,
 					Allowlist: []models.License{},
 				},
 			},
@@ -255,18 +276,19 @@ func Test_assembleResult(t *testing.T) {
 								},
 							},
 							Groups: []models.GroupInfo{
-								{IDs: []string{"CVE-123", "GHSA-123"}},
+								{
+									IDs:     []string{"CVE-123", "GHSA-123"},
+									Aliases: []string{"CVE-123", "GHSA-123"},
+								},
 							},
-							Licenses:          makeLicenses([]string{"MIT", "0BSD"}),
-							LicenseViolations: makeLicenses([]string{"MIT", "0BSD"}),
+							Licenses: makeLicenses([]string{"MIT", "0BSD"}),
 						}, {
 							Package: models.PackageInfo{
 								Name:      "pkg-2",
 								Ecosystem: "npm",
 								Version:   "1.0.0",
 							},
-							Licenses:          makeLicenses([]string{"MIT"}),
-							LicenseViolations: makeLicenses([]string{"MIT"}),
+							Licenses: makeLicenses([]string{"MIT"}),
 						},
 					},
 				},
@@ -286,10 +308,12 @@ func Test_assembleResult(t *testing.T) {
 								{ID: "GHSA-456"},
 							},
 							Groups: []models.GroupInfo{
-								{IDs: []string{"GHSA-456"}},
+								{
+									IDs:     []string{"GHSA-456"},
+									Aliases: []string{"GHSA-456"},
+								},
 							},
-							Licenses:          makeLicenses([]string{"UNKNOWN"}),
-							LicenseViolations: makeLicenses([]string{"UNKNOWN"}),
+							Licenses: makeLicenses([]string{"UNKNOWN"}),
 						},
 					},
 				},
@@ -298,19 +322,21 @@ func Test_assembleResult(t *testing.T) {
 	}, {
 		name: "group vulnerabilities with license allowlist",
 		args: args{
-			r:                 &reporter.VoidReporter{},
-			packages:          packages,
-			vulnsResp:         vulnsResp,
-			licensesResp:      licensesResp,
-			allPackages:       false,
-			callAnalysis:      false,
-			licenses:          true,
-			licensesAllowlist: []string{"MIT", "0BSD"},
+			r:            &reporter.VoidReporter{},
+			packages:     packages,
+			vulnsResp:    vulnsResp,
+			licensesResp: licensesResp,
+			actions: ScannerActions{
+				ExperimentalScannerActions: ExperimentalScannerActions{
+					ShowAllPackages:       false,
+					ScanLicensesAllowlist: []string{"MIT", "0BSD"},
+				},
+				CallAnalysisStates: callAnalysisStates,
+			},
 		},
 		want: models.VulnerabilityResults{
 			ExperimentalAnalysisConfig: models.ExperimentalAnalysisConfig{
 				Licenses: models.ExperimentalLicenseConfig{
-					Enabled:   true,
 					Allowlist: []models.License{models.License("MIT"), models.License("0BSD")},
 				},
 			},
@@ -337,7 +363,10 @@ func Test_assembleResult(t *testing.T) {
 								},
 							},
 							Groups: []models.GroupInfo{
-								{IDs: []string{"CVE-123", "GHSA-123"}},
+								{
+									IDs:     []string{"CVE-123", "GHSA-123"},
+									Aliases: []string{"CVE-123", "GHSA-123"},
+								},
 							},
 							Licenses: makeLicenses([]string{"MIT", "0BSD"}),
 						},
@@ -359,7 +388,10 @@ func Test_assembleResult(t *testing.T) {
 								{ID: "GHSA-456"},
 							},
 							Groups: []models.GroupInfo{
-								{IDs: []string{"GHSA-456"}},
+								{
+									IDs:     []string{"GHSA-456"},
+									Aliases: []string{"GHSA-456"},
+								},
 							},
 							Licenses:          makeLicenses([]string{"UNKNOWN"}),
 							LicenseViolations: makeLicenses([]string{"UNKNOWN"}),
@@ -371,19 +403,21 @@ func Test_assembleResult(t *testing.T) {
 	}, {
 		name: "group vulnerabilities, with license allowlist and all packages",
 		args: args{
-			r:                 &reporter.VoidReporter{},
-			packages:          packages,
-			vulnsResp:         vulnsResp,
-			licensesResp:      licensesResp,
-			allPackages:       true,
-			callAnalysis:      false,
-			licenses:          true,
-			licensesAllowlist: []string{"MIT", "0BSD"},
+			r:            &reporter.VoidReporter{},
+			packages:     packages,
+			vulnsResp:    vulnsResp,
+			licensesResp: licensesResp,
+			actions: ScannerActions{
+				ExperimentalScannerActions: ExperimentalScannerActions{
+					ShowAllPackages:       true,
+					ScanLicensesAllowlist: []string{"MIT", "0BSD"},
+				},
+				CallAnalysisStates: callAnalysisStates,
+			},
 		},
 		want: models.VulnerabilityResults{
 			ExperimentalAnalysisConfig: models.ExperimentalAnalysisConfig{
 				Licenses: models.ExperimentalLicenseConfig{
-					Enabled:   true,
 					Allowlist: []models.License{models.License("MIT"), models.License("0BSD")},
 				},
 			},
@@ -410,7 +444,10 @@ func Test_assembleResult(t *testing.T) {
 								},
 							},
 							Groups: []models.GroupInfo{
-								{IDs: []string{"CVE-123", "GHSA-123"}},
+								{
+									IDs:     []string{"CVE-123", "GHSA-123"},
+									Aliases: []string{"CVE-123", "GHSA-123"},
+								},
 							},
 							Licenses: makeLicenses([]string{"MIT", "0BSD"}),
 						}, {
@@ -439,7 +476,10 @@ func Test_assembleResult(t *testing.T) {
 								{ID: "GHSA-456"},
 							},
 							Groups: []models.GroupInfo{
-								{IDs: []string{"GHSA-456"}},
+								{
+									IDs:     []string{"GHSA-456"},
+									Aliases: []string{"GHSA-456"},
+								},
 							},
 							Licenses:          makeLicenses([]string{"UNKNOWN"}),
 							LicenseViolations: makeLicenses([]string{"UNKNOWN"}),
@@ -453,7 +493,7 @@ func Test_assembleResult(t *testing.T) {
 		tt := tt // Reinitialize for t.Parallel()
 		t.Run(tt.name, func(t *testing.T) {
 			t.Parallel()
-			if got := buildVulnerabilityResults(tt.args.r, tt.args.packages, tt.args.vulnsResp, tt.args.licensesResp, tt.args.callAnalysis, tt.args.allPackages, tt.args.licenses, tt.args.licensesAllowlist); !reflect.DeepEqual(got, tt.want) {
+			if got := buildVulnerabilityResults(tt.args.r, tt.args.packages, tt.args.vulnsResp, tt.args.licensesResp, tt.args.actions); !reflect.DeepEqual(got, tt.want) {
 				t.Errorf("buildVulnerabilityResults() = %v,\nwant %v", got, tt.want)
 			}
 		})