Repository: google/osv-scanner
Pull request #575 (2023-10-06 05:31:49)

Improve documentation for github actions
Attempt at the 3 remaining items mentioned in https://github.com/google/osv-scanner/issues/516. 

Feel free to do any rewording or rearranging @hayleycd to make things clearer!

Also fix the SARIF test since it was still calling `createTextFixture` instead of `assertTextFixture`

codecov-commenter: ## [Codecov](https://app.codecov.io/gh/google/osv-scanner/pull/575?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) Report
> Merging [#575](https://app.codecov.io/gh/google/osv-scanner/pull/575?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) (c75ac35) into [main](https://app.codecov.io/gh/google/osv-scanner/commit/4d86a90585bdbea1634b81c9b709a5a187a3c93b?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) (4d86a90) will **not change** coverage.
> The diff coverage is `n/a`.

```diff
@@           Coverage Diff           @@
##             main     #575   +/-   ##
=======================================
  Coverage   80.14%   80.14%           
=======================================
  Files          77       77           
  Lines        5314     5314           
=======================================
  Hits         4259     4259           
  Misses        881      881           
  Partials      174      174           
```




hayleycd: Can you build the docs from this branch so we can preview them? @another-rex 
hayleycd: The GitHub Action page is getting a little long, I would include a table of contents like some of the other pages have. I can put this in if you would like me to make my changes in a separate PR. 
another-rex: Should now be previewing this branch here: https://another-rex.github.io/osv-scanner/
another-rex: @hayleycd Updated your permissions to maintainer, you should be able to push to PR branches now.

Patch:
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 3d61bcb..dcd3334 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@ddccb873888234080b77e9bc2d4764d5ccaaccf9 # v2.21.9
+        uses: github/codeql-action/init@2cb752a87e96af96708ab57187ab6372ee1973ab # v2.22.0
         with:
           languages: ${{ matrix.language }}
           # If you wish to specify custom queries, you can do so here or in a config file.
@@ -55,7 +55,7 @@ jobs:
       # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
       # If this step fails, then you should remove it and run the build manually (see below)
       - name: Autobuild
-        uses: github/codeql-action/autobuild@ddccb873888234080b77e9bc2d4764d5ccaaccf9 # v2.21.9
+        uses: github/codeql-action/autobuild@2cb752a87e96af96708ab57187ab6372ee1973ab # v2.22.0
 
       # ℹ️ Command-line programs to run using the OS shell.
       # 📚 https://git.io/JvXDl
@@ -69,4 +69,4 @@ jobs:
       #   make release
 
       - name: Perform CodeQL Analysis
-        uses: github/codeql-action/analyze@ddccb873888234080b77e9bc2d4764d5ccaaccf9 # v2.21.9
+        uses: github/codeql-action/analyze@2cb752a87e96af96708ab57187ab6372ee1973ab # v2.22.0
diff --git a/.github/workflows/osv-scanner-reusable-pr.yml b/.github/workflows/osv-scanner-reusable-pr.yml
index ba0e4aa..ed570d3 100644
--- a/.github/workflows/osv-scanner-reusable-pr.yml
+++ b/.github/workflows/osv-scanner-reusable-pr.yml
@@ -24,6 +24,10 @@ on:
           -r
           --skip-git
           ./
+      results-file-name:
+        description: "File name of the result SARIF file"
+        type: string
+        default: results.sarif
 
 jobs:
   scan-pr:
@@ -57,7 +61,7 @@ jobs:
         uses: google/osv-scanner/actions/reporter@main
         with:
           scan-args: |-
-            --output=final-results.sarif
+            --output=${{ inputs.results-file-name }}
             --old=old-results.json
             --new=new-results.json
             --gh-annotations=true
@@ -68,7 +72,7 @@ jobs:
         uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
         with:
           name: SARIF file
-          path: final-results.sarif
+          path: ${{ inputs.results-file-name }}
           retention-days: 5
       - name: "Upload old scan json results"
         if: '!cancelled()'
@@ -87,6 +91,6 @@ jobs:
       # Upload the results to GitHub's code scanning dashboard.
       - name: "Upload to code-scanning"
         if: '!cancelled()'
-        uses: github/codeql-action/upload-sarif@ddccb873888234080b77e9bc2d4764d5ccaaccf9 # v2.21.9
+        uses: github/codeql-action/upload-sarif@2cb752a87e96af96708ab57187ab6372ee1973ab # v2.22.0
         with:
-          sarif_file: final-results.sarif
+          sarif_file: ${{ inputs.results-file-name }}
diff --git a/.github/workflows/osv-scanner-reusable.yml b/.github/workflows/osv-scanner-reusable.yml
index 5d55b21..8153029 100644
--- a/.github/workflows/osv-scanner-reusable.yml
+++ b/.github/workflows/osv-scanner-reusable.yml
@@ -30,7 +30,7 @@ on:
         default: results.sarif
 
 jobs:
-  scan-scheduled:
+  osv-scan:
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v4
@@ -60,7 +60,7 @@ jobs:
       # Upload the results to GitHub's code scanning dashboard.
       - name: "Upload to code-scanning"
         if: '!cancelled()'
-        uses: github/codeql-action/upload-sarif@ddccb873888234080b77e9bc2d4764d5ccaaccf9 # v2.21.9
+        uses: github/codeql-action/upload-sarif@2cb752a87e96af96708ab57187ab6372ee1973ab # v2.22.0
         with:
           sarif_file: ${{ inputs.results-file-name }}
 
diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml
index 30ee9fa..a97846b 100644
--- a/.github/workflows/scorecards.yml
+++ b/.github/workflows/scorecards.yml
@@ -37,7 +37,7 @@ jobs:
           persist-credentials: false
 
       - name: "Run analysis"
-        uses: ossf/scorecard-action@08b4669551908b1024bb425080c797723083c031 # v2.2.0
+        uses: ossf/scorecard-action@483ef80eb98fb506c348f7d62e28055e49fe2398 # v2.3.0
         with:
           results_file: results.sarif
           results_format: sarif
@@ -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@ddccb873888234080b77e9bc2d4764d5ccaaccf9 # v2.21.9
+        uses: github/codeql-action/upload-sarif@2cb752a87e96af96708ab57187ab6372ee1973ab # v2.22.0
         with:
           sarif_file: results.sarif
diff --git a/Dockerfile b/Dockerfile
index 9c4f255..61e9739 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-FROM golang:alpine@sha256:4bc6541af94a67d9dcabba9826c36e4e9497dacf4e8755ac503000b6ff75318f AS builder
+FROM golang:alpine@sha256:a76f153cff6a59112777c071b0cde1b6e4691ddc7f172be424228da1bfb7bbda AS builder
 
 WORKDIR /src
 COPY ./go.mod ./go.sum ./
diff --git a/action.dockerfile b/action.dockerfile
index 0b78adc..1f5609d 100644
--- a/action.dockerfile
+++ b/action.dockerfile
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-FROM golang:alpine@sha256:4bc6541af94a67d9dcabba9826c36e4e9497dacf4e8755ac503000b6ff75318f
+FROM golang:alpine@sha256:a76f153cff6a59112777c071b0cde1b6e4691ddc7f172be424228da1bfb7bbda
 
 RUN mkdir /src
 WORKDIR /src
diff --git a/docs/github-action.md b/docs/github-action.md
index d5d920e..fdd4f8e 100644
--- a/docs/github-action.md
+++ b/docs/github-action.md
@@ -11,9 +11,81 @@ nav_order: 6
 
 OSV-Scanner is offered as a GitHub Action. We currently have two different GitHub Actions:
 
-1. An action that performs a vulnerability scan on a [regular schedule](./github-action.md#scheduled-scans).
+1. An action that performs a single vulnerability scan, which can be configured to scan on a [regular schedule](./github-action.md#scheduled-scans), or used as a check [on releases](./github-action.md#scan-on-release) to prevent releasing with known vulnerabilities in dependencies.
 2. An action that triggers a scan with each [pull request](./github-action.md#scans-on-prs) and will only check for new vulnerabilities introduced through the pull request.
 
+## Scans on PRs
+
+Scanning your project on each pull request can help you keep vulnerabilities out of your project. This GitHub Action compares a vulnerability scan of the main branch to a vulnerability scan of the feature branch. You will be notified of any new vulnerabilities introduced through the feature branch. You can also choose to [prevent merging](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches#require-status-checks-before-merging) if new vulnerabilities are introduced through the feature branch.
+
+### Instructions
+
+In your project repository, create a new file `.github/workflows/osv-scanner-pr.yml`.
+
+Include the following in the [`osv-scanner-pr.yml`](https://github.com/google/osv-scanner/blob/main/.github/workflows/osv-scanner-pr.yml) file:
+
+```yml
+name: OSV-Scanner PR Scan
+
+# Change "main" to your default branch if you use a different name, i.e. "master"
+on:
+  pull_request:
+    branches: [ main ]
+  merge_group:
+    branches: [ main ]
+
+# Declare default permissions as read only.
+permissions: read-all
+
+jobs:
+  scan-pr:
+    uses: "google/osv-scanner/.github/workflows/osv-scanner-reusable-pr.yml@main"
+```
+
+### View results
+
+Results may be viewed by clicking on the details of the failed action, either from your project's actions tab or directly on the PR. Results are also included in GitHub annotations on the "Files changed" tab for the PR.
+
+### Customization
+
+`osv-scanner-reusable.yml` takes two optional inputs:
+
+- `scan-args`: This value is passed to `osv-scanner` CLI after being split by each line. See the [usage]()./usage) page for the available options.
+  Importantly `--format` and `--output` flags are already set by the reusable workflow and should not be overridden here.
+  Default:
+  ```
+    --recursive # Recursively scan subdirectories
+    --skip-git=true # Skip commit scanning to focus on dependencies
+    ./ # Start the scan from the root of the repository
+  ```
+- `results-file-name`: This is the name of the final SARIF file uploaded to Github.
+  Default: `results.sarif`
+
+#### Examples
+```yml
+# Scan specific lockfiles
+jobs:
+  scan-pr:
+    uses: "google/osv-scanner/.github/workflows/osv-scanner-reusable.yml"
+    with:
+      scan-args: |-
+        ./path/to/lockfile1
+        ./path/to/lockfile2
+```
+
+```yml
+# Default arguments
+jobs:
+  scan-pr:
+    uses: "google/osv-scanner/.github/workflows/osv-scanner-reusable.yml"
+    with:
+      scan-args: |-
+        --recursive
+        --skip-git=true
+        ./
+```
+
+
 ## Scheduled scans
 
 Regularly scanning your project for vulnerabilities can alert you to new vulnerabilities in your dependency tree. This GitHub Action will scan your project on a set schedule and report all known vulnerabilities.
@@ -47,40 +119,55 @@ jobs:
 
 As written, the scanner will run on 12:12 pm UTC every Monday. You can change the schedule by following the instructions [here](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule).
 
-### View results
+### Customization
 
-Maintainers can review results of the scan by navigating to their project's security > code scanning tab. Vulnerability details can also be viewed by clicking on the details of the failed action.
+`osv-scanner-reusable-pr.yml` has the same customization options as `osv-scanner-reusable.yml`, which is described [here](./github-action.md#customization).
 
-## Scans on PRs
+### View results
 
-Scanning your project on each pull request can help you keep vulnerabilities out of your project. This GitHub Action compares a vulnerability scan of the main branch to a vulnerability scan of the feature branch. You will be notified of any new vulnerabilities introduced through the feature branch. You can also choose to [prevent merging](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches#require-status-checks-before-merging) if new vulnerabilities are introduced through the feature branch.
+Maintainers can review results of the scan by navigating to their project's `security > code scanning` tab. Vulnerability details can also be viewed by clicking on the details of the failed action.
 
-### Instructions
 
-In your project repository, create a new file `.github/workflows/osv-scanner-pr.yml`.
+## Scan on release
 
-Include the following in the [`osv-scanner-pr.yml`](https://github.com/google/osv-scanner/blob/main/.github/workflows/osv-scanner-pr.yml) file:
+Here is a example of blocking on release, though the actual implementation will heavily depend on your specific release process.
 
 ```yml
-name: OSV-Scanner PR Scan
+name: Go Release Process
 
-# Change "main" to your default branch if you use a different name, i.e. "master"
 on:
-  pull_request:
-    branches: [ main ]
-  merge_group:
-    branches: [ main ]
+  push:
+    tags:
+      - "*" # triggers only if push new tag version, like `0.8.4` or else
 
-# Declare default permissions as read only.
-permissions: read-all
+permissions:
+  contents: read # to fetch code (actions/checkout)
 
 jobs:
-  scan-pr:
-    uses: "google/osv-scanner/.github/workflows/osv-scanner-reusable-pr.yml@main"
+  osv-scan:
+    uses: google/osv-scanner/.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
+        ./
+    permissions:
+      # Require writing security events to upload SARIF file to security tab
+      security-events: write
+  tests:
+    name: Run unit tests
+    ...
+  release:
+    needs: # Needs both tests and osv-scan to pass
+      - tests
+      - osv-scan
+    # Your actual release steps
+    steps:
+      ...
 ```
 
 ### View results
 
-Results may be viewed by clicking on the details of the failed action, either from your project's actions tab or directly on the PR. Results are also included in GitHub annotations on the "Files changed" tab for the PR.
+Results may be viewed by clicking on the details of the failed release action from the action tab.
 
-Results are also available to maintainers by navigating to their project's security > code scanning tab.
diff --git a/docs/output.md b/docs/output.md
index c4b4dde..b2b65bb 100644
--- a/docs/output.md
+++ b/docs/output.md
@@ -196,6 +196,156 @@ osv-scanner --format json -L path/to/lockfile > /path/to/file.json
 </details>
 
 
+---
+
+### SARIF
+
+```bash
+osv-scanner --format sarif your/project/dir
+```
+
+Outputs the result in the [SARIF](https://sarifweb.azurewebsites.net/) v2.1.0 format. Each vulnerability (grouped by aliases) is a separate rule, and each package containing a vulnerable dependency is a rule violation. The help text within the SARIF report contains detailed information about the vulnerability and remediation instructions for how to resolve it.
+
+<details markdown="1">
+<summary><b>Sample SARIF output</b></summary>
+
+```json
+{
+  "version": "2.1.0",
+  "$schema": "https://json.schemastore.org/sarif-2.1.0.json",
+  "runs": [
+    {
+      "tool": {
+        "driver": {
+          "informationUri": "https://github.com/google/osv-scanner",
+          "name": "osv-scanner",
+          "rules": [
+            {
+              "id": "CVE-2022-24713",
+              "shortDescription": {
+                "text": "CVE-2022-24713: <advisory summary>"
+              },
+              "fullDescription": {
+                "text": "<Full advisory details>...",
+                "markdown": "<Full advisory details>..."
+              },
+              // Deprecated IDs field contains all alias IDs
+              "deprecatedIds": [
+                "CVE-2022-24713",
+                "RUSTSEC-2022-0013",
+                "GHSA-m5pq-gvj9-9vr8"
+              ],
+              "help": {
+                "text": "<Markdown help text>...",
+                "markdown": "<Markdown help text>..."
+              }
+            },
+          ],
+          "version": "1.4.1"
+        }
+      },
+      "artifacts": [
+        {
+          "location": {
+            "uri": "file:///path/to/sub-rust-project/Cargo.lock"
+          },
+          "length": -1
+        }
+      ],
+      "results": [
+        {
+          "ruleId": "CVE-2022-24713",
+          "ruleIndex": 0,
+          "level": "warning",
+          "message": {
+            "text": "Package 'regex@1.5.1' is vulnerable to 'CVE-2022-24713' (also known as 'RUSTSEC-2022-0013', 'GHSA-m5pq-gvj9-9vr8')."
+          },
+          "locations": [
+            {
+              "physicalLocation": {
+                "artifactLocation": {
+                  "uri": "file:///path/to/sub-rust-project/Cargo.lock"
+                }
+              }
+            }
+          ]
+        }
+      ]
+    }
+  ]
+}
+```
+
+</details>
+
+<details markdown="1">
+<summary><b>Sample SARIF Help Text</b></summary>
+
+
+> **Your dependency is vulnerable to [CVE-2022-24713](https://osv.dev/list?q=CVE-2022-24713)**
+> (Also published as:  [RUSTSEC-2022-0013](https://osv.dev/vulnerability/RUSTSEC-2022-0013),  [GHSA-m5pq-gvj9-9vr8](https://osv.dev/vulnerability/GHSA-m5pq-gvj9-9vr8), ).
+>
+>
+> ## [RUSTSEC-2022-0013](https://osv.dev/vulnerability/RUSTSEC-2022-0013)
+>
+> <details>
+> <summary>Details</summary>
+>
+> > Full advisory details...
+>
+> </details>
+>
+>
+>
+> ## [GHSA-m5pq-gvj9-9vr8](https://osv.dev/vulnerability/GHSA-m5pq-gvj9-9vr8)
+>
+> <details>
+> <summary>Details</summary>
+>
+> > Full advisory details...
+>
+> </details>
+>
+>
+> ---
+>
+> ### Affected Packages
+> | Source | Package Name | Package Version |
+> | --- | --- | --- |
+> | lockfile:/path/to/rust-project/Cargo.lock | regex | 1.5.1 |
+>
+> ## Remediation
+>
+>
+>
+> To fix these vulnerabilities, update the vulnerabilities past the listed fixed versions below.
+>
+> ### Fixed Versions
+> | Vulnerability ID | Package Name | Fixed Version |
+> | --- | --- | --- |
+> | GHSA-m5pq-gvj9-9vr8 | regex | 1.5.5 |
+> | RUSTSEC-2022-0013 | regex | 1.5.5 |
+>
+>
+> If you believe these vulnerabilities do not affect your code and wish to ignore them, add them to the ignore list in an
+> `osv-scanner.toml` file located in the same directory as the lockfile containing the vulnerable dependency.
+>
+> See the format and more options in our documentation here: https://google.github.io/osv-scanner/configuration/
+>
+> Add or append these values to the following config files to ignore this vulnerability:
+>
+>
+> `/path/to/rust-project/osv-scanner.toml`
+> ```
+> [[IgnoredVulns]]
+> id = "CVE-2022-24713"
+> reason = "Your reason for ignoring this vulnerability"
+> ```
+
+</details>
+
+---
+
 ## Call analysis
 
 With `--experimental-call-analysis` flag enabled, call information will be included in the output.
@@ -367,12 +517,12 @@ osv-scanner --format json --experimental-call-analysis -L path/to/lockfile > /pa
 
 ## Return Codes
 
-|----- 
-| Exit Code |Reason| 
-|:---------------:|------------| 
-| `0` | Packages were found when scanning, but does not match any known vulnerabilities. | 
-| `1` | Packages were found when scanning, and there are vulnerabilities. | 
-| `1-126` | Reserved for vulnerability result related errors. | 
-| `127` | General Error. | 
-| `128` | No packages found (likely caused by the scanning format not picking up any files to scan). | 
-| `129-255` | Reserved for non result related errors. | 
+|-----
+| Exit Code |Reason|
+|:---------------:|------------|
+| `0` | Packages were found when scanning, but does not match any known vulnerabilities. |
+| `1` | Packages were found when scanning, and there are vulnerabilities. |
+| `1-126` | Reserved for vulnerability result related errors. |
+| `127` | General Error. |
+| `128` | No packages found (likely caused by the scanning format not picking up any files to scan). |
+| `129-255` | Reserved for non result related errors. |
diff --git a/pkg/lockfile/fixtures/yarn/with-build-string.v1.lock b/pkg/lockfile/fixtures/yarn/with-build-string.v1.lock
new file mode 100644
index 0000000..b95d949
--- /dev/null
+++ b/pkg/lockfile/fixtures/yarn/with-build-string.v1.lock
@@ -0,0 +1,13 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"domino@https://github.com/angular/domino.git#f2435fe1f9f7c91ade0bd472c4723e5eacd7d19a":
+  version "2.1.6+git"
+  resolved "git+ssh://git@github.com/angular/domino.git"
+  integrity sha512-1D4hj4hN9Q3Coi+gXZjFlxrm+7Jkhe/WBWU6loAQ/BqDWDRYwqQB1YkKbCVvfaVJXAB8OM2sae7KMyqM69pfRQ==
+
+tslib@^2.3.0:
+  version "2.6.2"
+  resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz"
+  integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
diff --git a/pkg/lockfile/fixtures/yarn/with-build-string.v2.lock b/pkg/lockfile/fixtures/yarn/with-build-string.v2.lock
new file mode 100644
index 0000000..de6dd4e
--- /dev/null
+++ b/pkg/lockfile/fixtures/yarn/with-build-string.v2.lock
@@ -0,0 +1,29 @@
+# This file is generated by running "yarn install" inside your project.
+# Manual changes might be lost - proceed with caution!
+
+__metadata:
+  version: 6
+  cacheKey: 8
+
+"domino@https://github.com/angular/domino.git#f2435fe1f9f7c91ade0bd472c4723e5eacd7d19a":
+  version: 2.1.6+git
+  resolution: "domino@https://github.com/angular/domino.git#commit=f2435fe1f9f7c91ade0bd472c4723e5eacd7d19a"
+  checksum: 0d9cacf8fd9ee104b9608dd1bb309520db40026e9b31533f3de5994a779bddbf0865820b67bc735c7dcb8067497c35835eb14c1656c31e0679d250f1cfd41073
+  languageName: node
+  linkType: hard
+
+"tslib@npm:^2.3.0":
+  version: 2.6.2
+  resolution: "tslib@npm:2.6.2"
+  checksum: 329ea56123005922f39642318e3d1f0f8265d1e7fcb92c633e0809521da75eeaca28d2cf96d7248229deb40e5c19adf408259f4b9640afd20d13aecc1430f3ad
+  languageName: node
+  linkType: hard
+
+"zone.js@workspace:.":
+  version: 0.0.0-use.local
+  resolution: "zone.js@workspace:."
+  dependencies:
+    domino: "https://github.com/angular/domino.git#f2435fe1f9f7c91ade0bd472c4723e5eacd7d19a"
+    tslib: ^2.3.0
+  languageName: unknown
+  linkType: soft
diff --git a/pkg/lockfile/parse-yarn-lock.go b/pkg/lockfile/parse-yarn-lock.go
index 5c764c7..43e6836 100644
--- a/pkg/lockfile/parse-yarn-lock.go
+++ b/pkg/lockfile/parse-yarn-lock.go
@@ -65,7 +65,7 @@ func extractYarnPackageName(str string) string {
 }
 
 func determineYarnPackageVersion(group []string) string {
-	re := cachedregexp.MustCompile(`^ {2}"?version"?:? "?([\w-.]+)"?$`)
+	re := cachedregexp.MustCompile(`^ {2}"?version"?:? "?([\w-.+]+)"?$`)
 
 	for _, s := range group {
 		matched := re.FindStringSubmatch(s)


Test patch:
diff --git a/internal/output/fixtures/test-vuln-results-a.sarif b/internal/output/fixtures/test-vuln-results-a.sarif
index ea1ca9f..dd0f499 100644
--- a/internal/output/fixtures/test-vuln-results-a.sarif
+++ b/internal/output/fixtures/test-vuln-results-a.sarif
@@ -47,7 +47,7 @@
               }
             }
           ],
-          "version": "1.4.0"
+          "version": "1.4.1"
         }
       },
       "artifacts": [
diff --git a/internal/output/sarif_test.go b/internal/output/sarif_test.go
index 99c4696..b609a53 100644
--- a/internal/output/sarif_test.go
+++ b/internal/output/sarif_test.go
@@ -67,7 +67,7 @@ func TestPrintSARIFReport(t *testing.T) {
 			if err != nil {
 				t.Errorf("Error writing SARIF output: %s", err)
 			}
-			testutility.CreateTextFixture(t, tt.wantPath, bufOut.String())
+			testutility.AssertMatchFixtureText(t, tt.wantPath, bufOut.String())
 		})
 	}
 }
diff --git a/pkg/lockfile/fixtures/yarn/versions-with-build-strings.v1.lock b/pkg/lockfile/fixtures/yarn/with-prerelease.v1.lock
similarity index 100%
rename from pkg/lockfile/fixtures/yarn/versions-with-build-strings.v1.lock
rename to pkg/lockfile/fixtures/yarn/with-prerelease.v1.lock
diff --git a/pkg/lockfile/fixtures/yarn/versions-with-build-strings.v2.lock b/pkg/lockfile/fixtures/yarn/with-prerelease.v2.lock
similarity index 100%
rename from pkg/lockfile/fixtures/yarn/versions-with-build-strings.v2.lock
rename to pkg/lockfile/fixtures/yarn/with-prerelease.v2.lock
diff --git a/pkg/lockfile/parse-yarn-lock-v1_test.go b/pkg/lockfile/parse-yarn-lock-v1_test.go
index 80e2679..c2363e7 100644
--- a/pkg/lockfile/parse-yarn-lock-v1_test.go
+++ b/pkg/lockfile/parse-yarn-lock-v1_test.go
@@ -183,10 +183,10 @@ func TestParseYarnLock_v1_ScopedPackages(t *testing.T) {
 	})
 }
 
-func TestParseYarnLock_v1_VersionsWithBuildString(t *testing.T) {
+func TestParseYarnLock_v1_WithPrerelease(t *testing.T) {
 	t.Parallel()
 
-	packages, err := lockfile.ParseYarnLock("fixtures/yarn/versions-with-build-strings.v1.lock")
+	packages, err := lockfile.ParseYarnLock("fixtures/yarn/with-prerelease.v1.lock")
 
 	if err != nil {
 		t.Errorf("Got unexpected error: %v", err)
@@ -226,6 +226,31 @@ func TestParseYarnLock_v1_VersionsWithBuildString(t *testing.T) {
 	})
 }
 
+func TestParseYarnLock_v1_WithBuildString(t *testing.T) {
+	t.Parallel()
+
+	packages, err := lockfile.ParseYarnLock("fixtures/yarn/with-build-string.v1.lock")
+
+	if err != nil {
+		t.Errorf("Got unexpected error: %v", err)
+	}
+
+	expectPackages(t, packages, []lockfile.PackageDetails{
+		{
+			Name:      "domino",
+			Version:   "2.1.6+git",
+			Ecosystem: lockfile.YarnEcosystem,
+			CompareAs: lockfile.YarnEcosystem,
+		},
+		{
+			Name:      "tslib",
+			Version:   "2.6.2",
+			Ecosystem: lockfile.YarnEcosystem,
+			CompareAs: lockfile.YarnEcosystem,
+		},
+	})
+}
+
 func TestParseYarnLock_v1_Commits(t *testing.T) {
 	t.Parallel()
 
diff --git a/pkg/lockfile/parse-yarn-lock-v2_test.go b/pkg/lockfile/parse-yarn-lock-v2_test.go
index 23d66ba..55959fa 100644
--- a/pkg/lockfile/parse-yarn-lock-v2_test.go
+++ b/pkg/lockfile/parse-yarn-lock-v2_test.go
@@ -158,10 +158,10 @@ func TestParseYarnLock_v2_ScopedPackages(t *testing.T) {
 	})
 }
 
-func TestParseYarnLock_v2_VersionsWithBuildString(t *testing.T) {
+func TestParseYarnLock_v2_WithPrerelease(t *testing.T) {
 	t.Parallel()
 
-	packages, err := lockfile.ParseYarnLock("fixtures/yarn/versions-with-build-strings.v2.lock")
+	packages, err := lockfile.ParseYarnLock("fixtures/yarn/with-prerelease.v2.lock")
 
 	if err != nil {
 		t.Errorf("Got unexpected error: %v", err)
@@ -189,6 +189,38 @@ func TestParseYarnLock_v2_VersionsWithBuildString(t *testing.T) {
 	})
 }
 
+func TestParseYarnLock_v2_WithBuildString(t *testing.T) {
+	t.Parallel()
+
+	packages, err := lockfile.ParseYarnLock("fixtures/yarn/with-build-string.v2.lock")
+
+	if err != nil {
+		t.Errorf("Got unexpected error: %v", err)
+	}
+
+	expectPackages(t, packages, []lockfile.PackageDetails{
+		{
+			Name:      "domino",
+			Version:   "2.1.6+git",
+			Commit:    "f2435fe1f9f7c91ade0bd472c4723e5eacd7d19a",
+			Ecosystem: lockfile.YarnEcosystem,
+			CompareAs: lockfile.YarnEcosystem,
+		},
+		{
+			Name:      "tslib",
+			Version:   "2.6.2",
+			Ecosystem: lockfile.YarnEcosystem,
+			CompareAs: lockfile.YarnEcosystem,
+		},
+		{
+			Name:      "zone.js",
+			Version:   "0.0.0-use.local",
+			Ecosystem: lockfile.YarnEcosystem,
+			CompareAs: lockfile.YarnEcosystem,
+		},
+	})
+}
+
 func TestParseYarnLock_v2_Commits(t *testing.T) {
 	t.Parallel()