Repository: google/osv-scanner
Pull request #224 (2023-02-19 13:17:03)

chore(deps): update dependency http_parser.rb to "~> 0.8.0"
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| http_parser.rb | patch | `"~> 0.6.0"` -> `"~> 0.8.0"` |

---

### Configuration

📅 **Schedule**: Branch creation - "before 6am on monday" in timezone Australia/Sydney, Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://app.renovatebot.com/dashboard#github/google/osv-scanner).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNC4xMjUuMSIsInVwZGF0ZWRJblZlciI6IjM0LjEyNS4xIn0=-->



Patch:
diff --git a/cmd/osv-scanner/fixtures/locks-insecure/my-yarn.lock b/cmd/osv-scanner/fixtures/locks-insecure/my-yarn.lock
new file mode 100644
index 0000000..50b5bb6
--- /dev/null
+++ b/cmd/osv-scanner/fixtures/locks-insecure/my-yarn.lock
@@ -0,0 +1,5 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+ansi-html@^0.0.1:
+  version "0.0.1"
diff --git a/docs/Gemfile b/docs/Gemfile
index 4ddbaad..3ef7eb7 100644
--- a/docs/Gemfile
+++ b/docs/Gemfile
@@ -29,5 +29,5 @@ gem "wdm", "~> 0.1.1", :platforms => [:mingw, :x64_mingw, :mswin]
 
 # Lock `http_parser.rb` gem to `v0.6.x` on JRuby builds since newer versions of the gem
 # do not have a Java counterpart.
-gem "http_parser.rb", "~> 0.6.0", :platforms => [:jruby]
+gem "http_parser.rb", "~> 0.8.0", :platforms => [:jruby]
 gem "webrick", "~> 1.7"
diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock
index 272d55b..431ef99 100644
--- a/docs/Gemfile.lock
+++ b/docs/Gemfile.lock
@@ -254,7 +254,7 @@ PLATFORMS
 
 DEPENDENCIES
   github-pages (~> 228)
-  http_parser.rb (~> 0.6.0)
+  http_parser.rb (~> 0.8.0)
   jekyll-feed (~> 0.12)
   tzinfo (>= 1, < 3)
   tzinfo-data
diff --git a/docs/_config.yml b/docs/_config.yml
index 3b0f003..a887b39 100644
--- a/docs/_config.yml
+++ b/docs/_config.yml
@@ -24,6 +24,7 @@ description: >- # this means to ignore newlines until "baseurl:"
 baseurl: "/osv-scanner" # the subpath of your site, e.g. /blog
 url: "" # the base hostname & protocol for your site, e.g. http://example.com
 github_username:  google
+ga_tracking: G-354127049
 
 # Build settings
 remote_theme: just-the-docs/just-the-docs
diff --git a/pkg/lockfile/apk-installed.go b/pkg/lockfile/apk-installed.go
index 5132897..ffe90b4 100644
--- a/pkg/lockfile/apk-installed.go
+++ b/pkg/lockfile/apk-installed.go
@@ -17,15 +17,14 @@ func groupApkPackageLines(scanner *bufio.Scanner) [][]string {
 	for scanner.Scan() {
 		line := scanner.Text()
 
-		if line == "" {
-			if len(group) > 0 {
-				groups = append(groups, group)
-			}
-			group = make([]string, 0)
-
+		if line != "" {
+			group = append(group, line)
 			continue
 		}
-		group = append(group, line)
+		if len(group) > 0 {
+			groups = append(groups, group)
+		}
+		group = make([]string, 0)
 	}
 
 	if len(group) > 0 {
@@ -56,7 +55,7 @@ func parseApkPackageGroup(group []string, pathToLockfile string) PackageDetails
 	if pkg.Version == "" {
 		pkgPrintName := pkg.Name
 		if pkgPrintName == "" {
-			pkgPrintName = "<unknown>"
+			pkgPrintName = unknownPkgName
 		}
 
 		_, _ = fmt.Fprintf(
diff --git a/pkg/lockfile/dpkg-status.go b/pkg/lockfile/dpkg-status.go
new file mode 100644
index 0000000..4fec193
--- /dev/null
+++ b/pkg/lockfile/dpkg-status.go
@@ -0,0 +1,162 @@
+package lockfile
+
+import (
+	"bufio"
+	"fmt"
+	"os"
+	"regexp"
+	"strings"
+)
+
+const DebianEcosystem Ecosystem = "Debian"
+
+func groupDpkgPackageLines(scanner *bufio.Scanner) [][]string {
+	var groups [][]string
+	var group []string
+
+	for scanner.Scan() {
+		line := scanner.Text()
+
+		if line != "" {
+			group = append(group, line)
+			continue
+		}
+		if len(group) > 0 {
+			groups = append(groups, group)
+		}
+		group = make([]string, 0)
+	}
+
+	if len(group) > 0 {
+		groups = append(groups, group)
+	}
+
+	return groups
+}
+
+// Return name and version if "Source" field contains them
+func parseSourceField(source string) (string, string) {
+	// Pattern: name (version)
+	re := regexp.MustCompile(`^(.*)\((.*)\)`)
+	matches := re.FindStringSubmatch(source)
+	if len(matches) == 3 {
+		return strings.TrimSpace(matches[1]), strings.TrimSpace(matches[2])
+	}
+	// If it not matches the pattern "name (version)", it is only "name"
+	return strings.TrimSpace(source), ""
+}
+
+func parseDpkgPackageGroup(group []string, pathToLockfile string) PackageDetails {
+	var pkg = PackageDetails{
+		Ecosystem: DebianEcosystem,
+		CompareAs: DebianEcosystem,
+	}
+
+	sourcePresent := false
+	sourceHasVersion := false
+	for _, line := range group {
+		switch {
+		// Status field SPECS: http://www.fifi.org/doc/libapt-pkg-doc/dpkg-tech.html/ch1.html#s1.2
+		case strings.HasPrefix(line, "Status:"):
+			status := strings.TrimPrefix(line, "Status:")
+			tokens := strings.Fields(status)
+			// Staus field is malformed. Expected: "Status: Want Flag Status"
+			if len(tokens) != 3 {
+				_, _ = fmt.Fprintf(
+					os.Stderr,
+					"warning: malformed DPKG status file. Found no valid \"Source\" field. File: %s\n",
+					pathToLockfile,
+				)
+
+				return PackageDetails{}
+			}
+			// Status field has correct number of fields but package is not installed or has only config files left
+			// various other field values indicate partial install/uninstall (e.g. failure of some pre/post install scripts)
+			// since it's not clear if failure has left package active on system, cautiously add it to queries to osv.dev
+			if tokens[2] == "not-installed" || tokens[2] == "config-files" {
+				return PackageDetails{}
+			}
+
+		case strings.HasPrefix(line, "Source:"):
+			sourcePresent = true
+			source := strings.TrimPrefix(line, "Source:")
+			name, version := parseSourceField(source)
+			pkg.Name = name // can be ""
+			if version != "" {
+				sourceHasVersion = true
+				pkg.Version = version
+			}
+
+		// If Source field has no version, use Version field
+		case strings.HasPrefix(line, "Version:"):
+			if !sourceHasVersion {
+				pkg.Version = strings.TrimPrefix(line, "Version:")
+				pkg.Version = strings.TrimSpace(pkg.Version)
+			}
+
+		// Some packages have no Source field (e.g. sudo) so we use Package value
+		case strings.HasPrefix(line, "Package:"):
+			if !sourcePresent {
+				pkg.Name = strings.TrimPrefix(line, "Package:")
+				pkg.Name = strings.TrimSpace(pkg.Name)
+			}
+		}
+	}
+
+	if pkg.Version == "" {
+		pkgPrintName := pkg.Name
+		if pkgPrintName == "" {
+			pkgPrintName = unknownPkgName
+		}
+
+		_, _ = fmt.Fprintf(
+			os.Stderr,
+			"warning: malformed DPKG status file. Found no version number in record. Package %s. File: %s\n",
+			pkgPrintName,
+			pathToLockfile,
+		)
+	}
+
+	return pkg
+}
+
+func ParseDpkgStatus(pathToLockfile string) ([]PackageDetails, error) {
+	file, err := os.Open(pathToLockfile)
+	if err != nil {
+		return []PackageDetails{}, fmt.Errorf("could not open %s: %w", pathToLockfile, err)
+	}
+	defer file.Close()
+
+	scanner := bufio.NewScanner(file)
+	packageGroups := groupDpkgPackageLines(scanner)
+
+	packages := make([]PackageDetails, 0, len(packageGroups))
+
+	for _, group := range packageGroups {
+		pkg := parseDpkgPackageGroup(group, pathToLockfile)
+
+		// PackageDetails does not contain any field that represent a "not installed" state
+		// To manage this state and avoid false positives, empty struct means "not installed" so skip it
+		if (PackageDetails{}) == pkg {
+			continue
+		}
+
+		if pkg.Name == "" {
+			_, _ = fmt.Fprintf(
+				os.Stderr,
+				"warning: malformed DPKG status file. Found no package name in record. File: %s\n",
+				pathToLockfile,
+			)
+
+			continue
+		}
+
+		packages = append(packages, pkg)
+	}
+
+	if err := scanner.Err(); err != nil {
+		return packages, fmt.Errorf("error while scanning %s: %w", pathToLockfile, err)
+	}
+
+	return packages, nil
+}
diff --git a/pkg/lockfile/fixtures/dpkg/empty_status b/pkg/lockfile/fixtures/dpkg/empty_status
new file mode 100644
index 0000000..e69de29
diff --git a/pkg/lockfile/fixtures/dpkg/malformed_status b/pkg/lockfile/fixtures/dpkg/malformed_status
new file mode 100644
index 0000000..cf4b500
--- /dev/null
+++ b/pkg/lockfile/fixtures/dpkg/malformed_status
@@ -0,0 +1,102 @@
+Malformed DPKG
+
+no version:
+
+Package: bash
+Essential: yes
+Status: install ok installed
+Priority: required
+Section: shells
+Installed-Size: 6469
+Maintainer: redacted <redacted@redacted.com>
+Architecture: amd64
+Multi-Arch: foreign
+Replaces: bash-completion (<< 20060301-0), bash-doc (<= 2.05-1)
+Depends: base-files (>= 2.1.12), debianutils (>= 2.15)
+Pre-Depends: libc6 (>= 2.25), libtinfo6 (>= 6)
+Recommends: bash-completion (>= 20060301-0)
+Suggests: bash-doc
+Conflicts: bash-completion (<< 20060301-0)
+Conffiles:
+ /etc/bash.bashrc 89269e1298235f1b12b4c16e4065ad0d
+ /etc/skel/.bash_logout 22bfb8c1dd94b5f3813a2b25da67463f
+ /etc/skel/.bashrc ee35a240758f374832e809ae0ea4883a
+ /etc/skel/.profile f4e81ade7d6f9fb342541152d08e7a97
+Description: GNU Bourne Again SHell
+ Bash is an sh-compatible command language interpreter that executes
+ commands read from the standard input or from a file.  Bash also
+ incorporates useful features from the Korn and C shells (ksh and csh).
+ .
+ Bash is ultimately intended to be a conformant implementation of the
+ IEEE POSIX Shell and Tools specification (IEEE Working Group 1003.2).
+ .
+ The Programmable Completion Code, by Ian Macdonald, is now found in
+ the bash-completion package.
+Homepage: http://tiswww.case.edu/php/chet/bash/bashtop.html
+
+
+
+No package:
+
+
+
+Essential: yes
+Status: install ok installed
+Priority: required
+Section: utils
+Installed-Size: 394
+Maintainer: redacted <redacted@redacted.com>
+Architecture: amd64
+Multi-Arch: foreign
+Source: util-linux (2.36.1-8+deb11u1)
+Version: 1:2.36.1-8+deb11u1
+Pre-Depends: libc6 (>= 2.17), libsystemd0
+Recommends: bsdextrautils
+Description: basic utilities from 4.4BSD-Lite
+ This package contains the bare minimum of BSD utilities needed for a Debian
+ system: logger, renice, script, scriptlive, scriptreplay and wall. The
+ remaining standard BSD utilities are provided by bsdextrautils.
+Homepage: http://www.kernel.org/pub/linux/utils/util-linux/
+
+#Bad status 
+
+Package: libc6
+Status: bad-status
+Priority: optional
+Section: libs
+Installed-Size: 12837
+Maintainer: redacted <redacted@redacted.com>
+Architecture: amd64
+Multi-Arch: same
+Source: glibc
+Version: 2.31-13+deb11u5
+Replaces: libc6-amd64
+Depends: libgcc-s1, libcrypt1
+Recommends: libidn2-0 (>= 2.0.5~), libnss-nis, libnss-nisplus
+Suggests: glibc-doc, debconf | debconf-2.0, libc-l10n, locales
+Breaks: busybox (<< 1.30.1-6), hurd (<< 1:0.9.git20170910-1), ioquake3 (<< 1.36+u20200211.f2c61c1~dfsg-2~), iraf-fitsutil (<< 2018.07.06-4), libgegl-0.4-0 (<< 0.4.18), libtirpc1 (<< 0.2.3), locales (<< 2.31), locales-all (<< 2.31), macs (<< 2.2.7.1-3~), nocache (<< 1.1-1~), nscd (<< 2.31), openarena (<< 0.8.8+dfsg-4~), openssh-server (<< 1:8.1p1-5), r-cran-later (<< 0.7.5+dfsg-2), wcc (<< 0.0.2+dfsg-3)
+Conffiles:
+ /etc/ld.so.conf.d/x86_64-linux-gnu.conf d4e7a7b88a71b5ffd9e2644e71a0cfab
+Description: GNU C Library: Shared libraries
+ Contains the standard libraries that are used by nearly all programs on
+ the system. This package includes shared versions of the standard C library
+ and the standard math library, as well as many others.
+Homepage: https://www.gnu.org/software/libc/libc.html
+
+
+Nothing...
+
+
+Status: install ok installed
+Priority: optional
+Section: libs
+Installed-Size: 12837
+Maintainer: redacted <redacted@redacted.com>
+Architecture: amd64
+Multi-Arch: same
+Replaces: libc6-amd64
+Description: GNU C Library: Shared libraries
+ Contains the standard libraries that are used by nearly all programs on
+ the system. This package includes shared versions of the standard C library
+ and the standard math library, as well as many others.
+Homepage: https://www.gnu.org/software/libc/libc.html
diff --git a/pkg/lockfile/fixtures/dpkg/multiple_status b/pkg/lockfile/fixtures/dpkg/multiple_status
new file mode 100644
index 0000000..59d8742
--- /dev/null
+++ b/pkg/lockfile/fixtures/dpkg/multiple_status
@@ -0,0 +1,99 @@
+Package: bash
+Essential: yes
+Status: install ok installed
+Priority: required
+Section: shells
+Installed-Size: 6469
+Maintainer: redacted <redacted@redacted.com>
+Architecture: amd64
+Multi-Arch: foreign
+Version: 5.1-2+deb11u1
+Replaces: bash-completion (<< 20060301-0), bash-doc (<= 2.05-1)
+Depends: base-files (>= 2.1.12), debianutils (>= 2.15)
+Pre-Depends: libc6 (>= 2.25), libtinfo6 (>= 6)
+Recommends: bash-completion (>= 20060301-0)
+Suggests: bash-doc
+Conflicts: bash-completion (<< 20060301-0)
+Conffiles:
+ /etc/bash.bashrc 89269e1298235f1b12b4c16e4065ad0d
+ /etc/skel/.bash_logout 22bfb8c1dd94b5f3813a2b25da67463f
+ /etc/skel/.bashrc ee35a240758f374832e809ae0ea4883a
+ /etc/skel/.profile f4e81ade7d6f9fb342541152d08e7a97
+Description: GNU Bourne Again SHell
+ Bash is an sh-compatible command language interpreter that executes
+ commands read from the standard input or from a file.  Bash also
+ incorporates useful features from the Korn and C shells (ksh and csh).
+ .
+ Bash is ultimately intended to be a conformant implementation of the
+ IEEE POSIX Shell and Tools specification (IEEE Working Group 1003.2).
+ .
+ The Programmable Completion Code, by Ian Macdonald, is now found in
+ the bash-completion package.
+Homepage: http://tiswww.case.edu/php/chet/bash/bashtop.html
+
+Package: bsdutils
+Essential: yes
+Status: install ok installed
+Priority: required
+Section: utils
+Installed-Size: 394
+Maintainer: redacted <redacted@redacted.com>
+Architecture: amd64
+Multi-Arch: foreign
+Source: util-linux (2.36.1-8+deb11u1)
+Version: 1:2.36.1-8+deb11u1
+Pre-Depends: libc6 (>= 2.17), libsystemd0
+Recommends: bsdextrautils
+Description: basic utilities from 4.4BSD-Lite
+ This package contains the bare minimum of BSD utilities needed for a Debian
+ system: logger, renice, script, scriptlive, scriptreplay and wall. The
+ remaining standard BSD utilities are provided by bsdextrautils.
+Homepage: http://www.kernel.org/pub/linux/utils/util-linux/
+
+Package: libc6
+Status: install ok installed
+Priority: optional
+Section: libs
+Installed-Size: 12837
+Maintainer: redacted <redacted@redacted.com>
+Architecture: amd64
+Multi-Arch: same
+Source: glibc
+Version: 2.31-13+deb11u5
+Replaces: libc6-amd64
+Depends: libgcc-s1, libcrypt1
+Recommends: libidn2-0 (>= 2.0.5~), libnss-nis, libnss-nisplus
+Suggests: glibc-doc, debconf | debconf-2.0, libc-l10n, locales
+Breaks: busybox (<< 1.30.1-6), hurd (<< 1:0.9.git20170910-1), ioquake3 (<< 1.36+u20200211.f2c61c1~dfsg-2~), iraf-fitsutil (<< 2018.07.06-4), libgegl-0.4-0 (<< 0.4.18), libtirpc1 (<< 0.2.3), locales (<< 2.31), locales-all (<< 2.31), macs (<< 2.2.7.1-3~), nocache (<< 1.1-1~), nscd (<< 2.31), openarena (<< 0.8.8+dfsg-4~), openssh-server (<< 1:8.1p1-5), r-cran-later (<< 0.7.5+dfsg-2), wcc (<< 0.0.2+dfsg-3)
+Conffiles:
+ /etc/ld.so.conf.d/x86_64-linux-gnu.conf d4e7a7b88a71b5ffd9e2644e71a0cfab
+Description: GNU C Library: Shared libraries
+ Contains the standard libraries that are used by nearly all programs on
+ the system. This package includes shared versions of the standard C library
+ and the standard math library, as well as many others.
+Homepage: https://www.gnu.org/software/libc/libc.html
+
+#Status not installed
+
+Package: libc6
+Status: install ok not-installed
+Priority: optional
+Section: libs
+Installed-Size: 12837
+Maintainer: redacted <redacted@redacted.com>
+Architecture: amd64
+Multi-Arch: same
+Source: glibc
+Version: 2.31-13+deb11u5
+Replaces: libc6-amd64
+Depends: libgcc-s1, libcrypt1
+Recommends: libidn2-0 (>= 2.0.5~), libnss-nis, libnss-nisplus
+Suggests: glibc-doc, debconf | debconf-2.0, libc-l10n, locales
+Breaks: busybox (<< 1.30.1-6), hurd (<< 1:0.9.git20170910-1), ioquake3 (<< 1.36+u20200211.f2c61c1~dfsg-2~), iraf-fitsutil (<< 2018.07.06-4), libgegl-0.4-0 (<< 0.4.18), libtirpc1 (<< 0.2.3), locales (<< 2.31), locales-all (<< 2.31), macs (<< 2.2.7.1-3~), nocache (<< 1.1-1~), nscd (<< 2.31), openarena (<< 0.8.8+dfsg-4~), openssh-server (<< 1:8.1p1-5), r-cran-later (<< 0.7.5+dfsg-2), wcc (<< 0.0.2+dfsg-3)
+Conffiles:
+ /etc/ld.so.conf.d/x86_64-linux-gnu.conf d4e7a7b88a71b5ffd9e2644e71a0cfab
+Description: GNU C Library: Shared libraries
+ Contains the standard libraries that are used by nearly all programs on
+ the system. This package includes shared versions of the standard C library
+ and the standard math library, as well as many others.
+Homepage: https://www.gnu.org/software/libc/libc.html
\ No newline at end of file
diff --git a/pkg/lockfile/fixtures/dpkg/not_status b/pkg/lockfile/fixtures/dpkg/not_status
new file mode 100644
index 0000000..a245542
--- /dev/null
+++ b/pkg/lockfile/fixtures/dpkg/not_status
@@ -0,0 +1,26 @@
+Not a dpkg status file!
+
+
+astroid==2.5.1
+    # via pylint
+beautifulsoup4==4.9.3
+    # via metadata-parser
+boto3==1.17.19
+    # via -r requirements.in
+botocore==1.20.19
+    # via
+    #   boto3
+    #   s3transfer
+certifi==2020.12.5
+    # via requests
+chardet==4.0.0
+    # via requests
+circus==0.17.1
+    # via -r requirements.in
+click==7.1.2
+    # via pip-tools
+django-debug-toolbar==3.2.1
+    # via -r requirements.in
+django-filter==2.4.0
+    # via -r requirements.in
+django-nose==1.4.7
\ No newline at end of file
diff --git a/pkg/lockfile/fixtures/dpkg/shuffled_status b/pkg/lockfile/fixtures/dpkg/shuffled_status
new file mode 100644
index 0000000..d875ac5
--- /dev/null
+++ b/pkg/lockfile/fixtures/dpkg/shuffled_status
@@ -0,0 +1,22 @@
+Breaks: busybox (<< 1.30.1-6), hurd (<< 1:0.9.git20170910-1), ioquake3 (<< 1.36+u20200211.f2c61c1~dfsg-2~), iraf-fitsutil (<< 2018.07.06-4), libgegl-0.4-0 (<< 0.4.18), libtirpc1 (<< 0.2.3), locales (<< 2.31), locales-all (<< 2.31), macs (<< 2.2.7.1-3~), nocache (<< 1.1-1~), nscd (<< 2.31), openarena (<< 0.8.8+dfsg-4~), openssh-server (<< 1:8.1p1-5), r-cran-later (<< 0.7.5+dfsg-2), wcc (<< 0.0.2+dfsg-3)
+Status: install ok installed
+Architecture: amd64
+Suggests: glibc-doc, debconf | debconf-2.0, libc-l10n, locales
+ Contains the standard libraries that are used by nearly all programs on
+Depends: libgcc-s1, libcrypt1
+Version: 2.31-13+deb11u5
+Description: GNU C Library: Shared libraries
+Replaces: libc6-amd64
+Installed-Size: 12837
+Section: libs
+Multi-Arch: same
+Maintainer: redacted <redacted@redacted.com>
+Homepage: https://www.gnu.org/software/libc/libc.html
+ /etc/ld.so.conf.d/x86_64-linux-gnu.conf d4e7a7b88a71b5ffd9e2644e71a0cfab
+Conffiles:
+Source: glibc
+ and the standard math library, as well as many others.
+Recommends: libidn2-0 (>= 2.0.5~), libnss-nis, libnss-nisplus
+ the system. This package includes shared versions of the standard C library
+Priority: optional
+Package: libc6
\ No newline at end of file
diff --git a/pkg/lockfile/fixtures/dpkg/single_status b/pkg/lockfile/fixtures/dpkg/single_status
new file mode 100644
index 0000000..12d1d03
--- /dev/null
+++ b/pkg/lockfile/fixtures/dpkg/single_status
@@ -0,0 +1,33 @@
+Package: sudo
+Status: install ok installed
+Priority: important
+Section: admin
+Installed-Size: 4337
+Maintainer: redacted <redacted@redacted.com>
+Architecture: amd64
+Version: 1.8.27-1+deb10u1
+Replaces: apt-transport-https (<< 1.5~alpha4~), apt-utils (<< 1.3~exp2~)
+Provides: apt-transport-https (= 2.2.4)
+Depends: adduser, gpgv | gpgv2 | gpgv1, libapt-pkg6.0 (>= 2.2.4), debian-archive-keyring, libc6 (>= 2.15), libgcc-s1 (>= 3.0), libgnutls30 (>= 3.7.0), libseccomp2 (>= 2.4.2), libstdc++6 (>= 9), libsystemd0
+Recommends: ca-certificates
+Suggests: apt-doc, aptitude | synaptic | wajig, dpkg-dev (>= 1.17.2), gnupg | gnupg2 | gnupg1, powermgmt-base
+Breaks: apt-transport-https (<< 1.5~alpha4~), apt-utils (<< 1.3~exp2~), aptitude (<< 0.8.10)
+Conffiles:
+ /etc/apt/apt.conf.d/01autoremove ab6540f7278a05a4b7f9e58afcaa5f46
+ /etc/cron.daily/apt-compat 49e9b2cfa17849700d4db735d04244f3
+ /etc/kernel/postinst.d/apt-auto-removal 6486b24d4c496e7d6a443178869a019b
+ /etc/logrotate.d/apt 179f2ed4f85cbaca12fa3d69c2a4a1c3
+Description: commandline package manager
+ This package provides commandline tools for searching and
+ managing as well as querying information about packages
+ as a low-level access to all features of the libapt-pkg library.
+ .
+ These include:
+  * apt-get for retrieval of packages and information about them
+    from authenticated sources and for installation, upgrade and
+    removal of packages together with their dependencies
+  * apt-cache for querying available information about installed
+    as well as installable packages
+  * apt-cdrom to use removable media as a source for packages
+  * apt-config as an interface to the configuration settings
+  * apt-key as an interface to manage authentication keys
\ No newline at end of file
diff --git a/pkg/lockfile/fixtures/dpkg/source_ver_override_status b/pkg/lockfile/fixtures/dpkg/source_ver_override_status
new file mode 100644
index 0000000..feb9fc1
--- /dev/null
+++ b/pkg/lockfile/fixtures/dpkg/source_ver_override_status
@@ -0,0 +1,20 @@
+Depends: libc6 (>= 2.14), libdevmapper-event1.02.1 (>= 2:1.02.110), libdevmapper1.02.1 (>= 2:1.02.141), liblvm2cmd2.02 (>= 2.02.176)
+Architecture: amd64
+ volume management, while keeping knowledge of the underlying device layout
+Section: admin
+Version: 2:1.02.145-4.1ubuntu3
+Original-Maintainer: redacted <redacted@redacted.com>
+ in user-space.  This makes it useful for not only LVM, but software raid,
+ .
+ The Linux Kernel Device Mapper is the LVM (Linux Logical Volume Management)
+Source: lvm2 (2.02.176-4.1ubuntu3)
+Description: Linux Kernel Device Mapper event daemon
+Maintainer: redacted <redacted@redacted.com>
+Status: install ok installed
+Homepage: http://sources.redhat.com/lvm2/
+Package: dmeventd
+Installed-Size: 188
+ This package contains a daemon to monitor events of devmapper devices.
+Priority: optional
+ and other drivers that create "virtual" block devices.
+ Team's implementation of a minimalistic kernel-space driver that handles
\ No newline at end of file
diff --git a/pkg/lockfile/fixtures/maven/interpolation.xml b/pkg/lockfile/fixtures/maven/interpolation.xml
index e9760a0..eb783c8 100644
--- a/pkg/lockfile/fixtures/maven/interpolation.xml
+++ b/pkg/lockfile/fixtures/maven/interpolation.xml
@@ -26,11 +26,15 @@
       <artifactId>my.package</artifactId>
       <version>${my.package.version}</version>
     </dependency>
-
-    <dependency>
-      <groupId>org.mine</groupId>
-      <artifactId>ranged-package</artifactId>
-      <version>${version-range}</version>
-    </dependency>
   </dependencies>
+
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>org.mine</groupId>
+        <artifactId>ranged-package</artifactId>
+        <version>${version-range}</version>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
 </project>
diff --git a/pkg/lockfile/fixtures/maven/with-dependency-management.xml b/pkg/lockfile/fixtures/maven/with-dependency-management.xml
new file mode 100644
index 0000000..76f419c
--- /dev/null
+++ b/pkg/lockfile/fixtures/maven/with-dependency-management.xml
@@ -0,0 +1,33 @@
+<project>
+  <properties>
+    <mavenVersion>3.0</mavenVersion>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>io.netty</groupId>
+      <artifactId>netty-all</artifactId>
+      <version>4.1.9</version>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+      <version>1.7.25</version>
+    </dependency>
+  </dependencies>
+
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>io.netty</groupId>
+        <artifactId>netty-all</artifactId>
+        <version>4.1.42.Final</version>
+      </dependency>
+      <dependency>
+        <groupId>com.google.code.findbugs</groupId>
+        <artifactId>jsr305</artifactId>
+        <version>3.0.2</version>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+</project>
diff --git a/pkg/lockfile/parse-maven-lock.go b/pkg/lockfile/parse-maven-lock.go
index 49abe84..000d90b 100644
--- a/pkg/lockfile/parse-maven-lock.go
+++ b/pkg/lockfile/parse-maven-lock.go
@@ -56,10 +56,11 @@ func (mld MavenLockDependency) ResolveVersion(lockfile MavenLockFile) string {
 }
 
 type MavenLockFile struct {
-	XMLName      xml.Name              `xml:"project"`
-	ModelVersion string                `xml:"modelVersion"`
-	Properties   MavenLockProperties   `xml:"properties"`
-	Dependencies []MavenLockDependency `xml:"dependencies>dependency"`
+	XMLName             xml.Name              `xml:"project"`
+	ModelVersion        string                `xml:"modelVersion"`
+	Properties          MavenLockProperties   `xml:"properties"`
+	Dependencies        []MavenLockDependency `xml:"dependencies>dependency"`
+	ManagedDependencies []MavenLockDependency `xml:"dependencyManagement>dependencies>dependency"`
 }
 
 const MavenEcosystem Ecosystem = "Maven"
@@ -107,16 +108,30 @@ func ParseMavenLock(pathToLockfile string) ([]PackageDetails, error) {
 		return []PackageDetails{}, fmt.Errorf("could not parse %s: %w", pathToLockfile, err)
 	}
 
-	packages := make([]PackageDetails, 0, len(parsedLockfile.Dependencies))
+	details := map[string]PackageDetails{}
 
 	for _, lockPackage := range parsedLockfile.Dependencies {
-		packages = append(packages, PackageDetails{
-			Name:      lockPackage.GroupID + ":" + lockPackage.ArtifactID,
+		finalName := lockPackage.GroupID + ":" + lockPackage.ArtifactID
+
+		details[finalName] = PackageDetails{
+			Name:      finalName,
 			Version:   lockPackage.ResolveVersion(*parsedLockfile),
 			Ecosystem: MavenEcosystem,
 			CompareAs: MavenEcosystem,
-		})
+		}
+	}
+
+	// managed dependencies take precedent over standard dependencies
+	for _, lockPackage := range parsedLockfile.ManagedDependencies {
+		finalName := lockPackage.GroupID + ":" + lockPackage.ArtifactID
+
+		details[finalName] = PackageDetails{
+			Name:      finalName,
+			Version:   lockPackage.ResolveVersion(*parsedLockfile),
+			Ecosystem: MavenEcosystem,
+			CompareAs: MavenEcosystem,
+		}
 	}
 
-	return packages, nil
+	return pkgDetailsMapToSlice(details), nil
 }
diff --git a/pkg/lockfile/parse.go b/pkg/lockfile/parse.go
index b07d561..6cda3eb 100644
--- a/pkg/lockfile/parse.go
+++ b/pkg/lockfile/parse.go
@@ -8,6 +8,11 @@ import (
 	"strings"
 )
 
+// Represents when a package name could not be determined while parsing.
+// Currently, parsers are expected to omit such packages from their results.
+// Using a const is required to avoid linter error (goconst) due to multiple usage in parsers.
+const unknownPkgName = "<unknown>"
+
 func FindParser(pathToLockfile string, parseAs string) (PackageDetailsParser, string) {
 	if parseAs == "" {
 		parseAs = filepath.Base(pathToLockfile)
diff --git a/pkg/osvscanner/vulnerability_result.go b/pkg/osvscanner/vulnerability_result.go
index 7e5a198..5196aae 100644
--- a/pkg/osvscanner/vulnerability_result.go
+++ b/pkg/osvscanner/vulnerability_result.go
@@ -2,6 +2,7 @@ package osvscanner
 
 import (
 	"fmt"
+	"sort"
 
 	"github.com/google/osv-scanner/internal/output"
 	"github.com/google/osv-scanner/pkg/grouper"
@@ -58,5 +59,13 @@ func groupResponseBySource(r *output.Reporter, query osv.BatchedQuery, resp *osv
 		})
 	}
 
+	sort.Slice(output.Results, func(i, j int) bool {
+		if output.Results[i].Source.Path == output.Results[j].Source.Path {
+			return output.Results[i].Source.Type < output.Results[j].Source.Type
+		}
+
+		return output.Results[i].Source.Path < output.Results[j].Source.Path
+	})
+
 	return output
 }


Test patch:
diff --git a/cmd/osv-scanner/main_test.go b/cmd/osv-scanner/main_test.go
index 5d96915..7c6b40d 100644
--- a/cmd/osv-scanner/main_test.go
+++ b/cmd/osv-scanner/main_test.go
@@ -352,6 +352,53 @@ func TestRun_LockfileWithExplicitParseAs(t *testing.T) {
 			`,
 			wantStderr: "",
 		},
+		// multiple, + output order is deterministic
+		{
+			name: "",
+			args: []string{
+				"",
+				"-L", "package-lock.json:" + filepath.FromSlash("./fixtures/locks-insecure/my-package-lock.json"),
+				"-L", "yarn.lock:" + filepath.FromSlash("./fixtures/locks-insecure/my-yarn.lock"),
+				filepath.FromSlash("./fixtures/locks-insecure"),
+			},
+			wantExitCode: 1,
+			wantStdout: `
+				Scanned %%/fixtures/locks-insecure/my-package-lock.json file as a package-lock.json and found 1 packages
+				Scanned %%/fixtures/locks-insecure/my-yarn.lock file as a yarn.lock and found 1 packages
+				Scanning dir ./fixtures/locks-insecure
+				Scanned %%/fixtures/locks-insecure/composer.lock file and found 0 packages
+				+-------------------------------------+-----------+-----------+---------+----------------------------------------------+
+				| OSV URL (ID IN BOLD)                | ECOSYSTEM | PACKAGE   | VERSION | SOURCE                                       |
+				+-------------------------------------+-----------+-----------+---------+----------------------------------------------+
+				| https://osv.dev/GHSA-whgm-jr23-g3j9 | npm       | ansi-html | 0.0.1   | fixtures/locks-insecure/my-package-lock.json |
+				| https://osv.dev/GHSA-whgm-jr23-g3j9 | npm       | ansi-html | 0.0.1   | fixtures/locks-insecure/my-yarn.lock         |
+				+-------------------------------------+-----------+-----------+---------+----------------------------------------------+
+			`,
+			wantStderr: "",
+		},
+		{
+			name: "",
+			args: []string{
+				"",
+				"-L", "yarn.lock:" + filepath.FromSlash("./fixtures/locks-insecure/my-yarn.lock"),
+				"-L", "package-lock.json:" + filepath.FromSlash("./fixtures/locks-insecure/my-package-lock.json"),
+				filepath.FromSlash("./fixtures/locks-insecure"),
+			},
+			wantExitCode: 1,
+			wantStdout: `
+				Scanned %%/fixtures/locks-insecure/my-yarn.lock file as a yarn.lock and found 1 packages
+				Scanned %%/fixtures/locks-insecure/my-package-lock.json file as a package-lock.json and found 1 packages
+				Scanning dir ./fixtures/locks-insecure
+				Scanned %%/fixtures/locks-insecure/composer.lock file and found 0 packages
+				+-------------------------------------+-----------+-----------+---------+----------------------------------------------+
+				| OSV URL (ID IN BOLD)                | ECOSYSTEM | PACKAGE   | VERSION | SOURCE                                       |
+				+-------------------------------------+-----------+-----------+---------+----------------------------------------------+
+				| https://osv.dev/GHSA-whgm-jr23-g3j9 | npm       | ansi-html | 0.0.1   | fixtures/locks-insecure/my-package-lock.json |
+				| https://osv.dev/GHSA-whgm-jr23-g3j9 | npm       | ansi-html | 0.0.1   | fixtures/locks-insecure/my-yarn.lock         |
+				+-------------------------------------+-----------+-----------+---------+----------------------------------------------+
+			`,
+			wantStderr: "",
+		},
 		// files that error on parsing stop parsable files from being checked
 		{
 			name: "",
diff --git a/pkg/lockfile/apk-installed_test.go b/pkg/lockfile/apk-installed_test.go
index e644232..d713a41 100644
--- a/pkg/lockfile/apk-installed_test.go
+++ b/pkg/lockfile/apk-installed_test.go
@@ -6,7 +6,7 @@ import (
 	"github.com/google/osv-scanner/pkg/lockfile"
 )
 
-func TestApkInstalled_FileDoesNotExist(t *testing.T) {
+func TestParseApkInstalled_FileDoesNotExist(t *testing.T) {
 	t.Parallel()
 
 	packages, err := lockfile.ParseApkInstalled("fixtures/apk/does-not-exist")
@@ -15,7 +15,7 @@ func TestApkInstalled_FileDoesNotExist(t *testing.T) {
 	expectPackages(t, packages, []lockfile.PackageDetails{})
 }
 
-func TestApkInstalled_Empty(t *testing.T) {
+func TestParseApkInstalled_Empty(t *testing.T) {
 	t.Parallel()
 
 	packages, err := lockfile.ParseApkInstalled("fixtures/apk/empty_installed")
@@ -27,7 +27,7 @@ func TestApkInstalled_Empty(t *testing.T) {
 	expectPackages(t, packages, []lockfile.PackageDetails{})
 }
 
-func TestApkInstalled_NotAnInstalled(t *testing.T) {
+func TestParseApkInstalled_NotAnInstalled(t *testing.T) {
 	t.Parallel()
 
 	packages, err := lockfile.ParseApkInstalled("fixtures/apk/not_installed")
@@ -39,7 +39,7 @@ func TestApkInstalled_NotAnInstalled(t *testing.T) {
 	expectPackages(t, packages, []lockfile.PackageDetails{})
 }
 
-func TestApkInstalled_Malformed(t *testing.T) {
+func TestParseApkInstalled_Malformed(t *testing.T) {
 	t.Parallel()
 
 	packages, err := lockfile.ParseApkInstalled("fixtures/apk/malformed_installed")
@@ -59,7 +59,7 @@ func TestApkInstalled_Malformed(t *testing.T) {
 	})
 }
 
-func TestApkInstalled_Single(t *testing.T) {
+func TestParseApkInstalled_Single(t *testing.T) {
 	t.Parallel()
 
 	packages, err := lockfile.ParseApkInstalled("fixtures/apk/single_installed")
@@ -79,7 +79,7 @@ func TestApkInstalled_Single(t *testing.T) {
 	})
 }
 
-func TestApkInstalled_Shuffled(t *testing.T) {
+func TestParseApkInstalled_Shuffled(t *testing.T) {
 	t.Parallel()
 
 	packages, err := lockfile.ParseApkInstalled("fixtures/apk/shuffled_installed")
@@ -99,7 +99,7 @@ func TestApkInstalled_Shuffled(t *testing.T) {
 	})
 }
 
-func TestApkInstalled_Multiple(t *testing.T) {
+func TestParseApkInstalled_Multiple(t *testing.T) {
 	t.Parallel()
 
 	packages, err := lockfile.ParseApkInstalled("fixtures/apk/multiple_installed")
diff --git a/pkg/lockfile/dpkg-status_test.go b/pkg/lockfile/dpkg-status_test.go
new file mode 100644
index 0000000..c6603c4
--- /dev/null
+++ b/pkg/lockfile/dpkg-status_test.go
@@ -0,0 +1,153 @@
+package lockfile_test
+
+import (
+	"testing"
+
+	"github.com/google/osv-scanner/pkg/lockfile"
+)
+
+func TestParseDpkgStatus_FileDoesNotExist(t *testing.T) {
+	t.Parallel()
+
+	packages, err := lockfile.ParseDpkgStatus("fixtures/dpkg/does-not-exist")
+
+	expectErrContaining(t, err, "could not open")
+	expectPackages(t, packages, []lockfile.PackageDetails{})
+}
+
+func TestParseDpkgStatus_Empty(t *testing.T) {
+	t.Parallel()
+
+	packages, err := lockfile.ParseDpkgStatus("fixtures/dpkg/empty_status")
+
+	if err != nil {
+		t.Errorf("Got unexpected error: %v", err)
+	}
+
+	expectPackages(t, packages, []lockfile.PackageDetails{})
+}
+
+func TestParseDpkgStatus_NotAStatus(t *testing.T) {
+	t.Parallel()
+
+	packages, err := lockfile.ParseDpkgStatus("fixtures/dpkg/not_status")
+
+	if err != nil {
+		t.Errorf("Got unexpected error: %v", err)
+	}
+
+	expectPackages(t, packages, []lockfile.PackageDetails{})
+}
+
+func TestParseDpkgStatus_Malformed(t *testing.T) {
+	t.Parallel()
+
+	packages, err := lockfile.ParseDpkgStatus("fixtures/dpkg/malformed_status")
+
+	if err != nil {
+		t.Errorf("Got unexpected error: %v", err)
+	}
+
+	expectPackages(t, packages, []lockfile.PackageDetails{
+		{
+			Name:      "bash",
+			Version:   "",
+			Ecosystem: lockfile.DebianEcosystem,
+			CompareAs: lockfile.DebianEcosystem,
+		},
+		{
+			Name:      "util-linux",
+			Version:   "2.36.1-8+deb11u1",
+			Ecosystem: lockfile.DebianEcosystem,
+			CompareAs: lockfile.DebianEcosystem,
+		},
+	})
+}
+
+func TestParseDpkgStatus_Single(t *testing.T) {
+	t.Parallel()
+
+	packages, err := lockfile.ParseDpkgStatus("fixtures/dpkg/single_status")
+
+	if err != nil {
+		t.Errorf("Got unexpected error: %v", err)
+	}
+
+	expectPackages(t, packages, []lockfile.PackageDetails{
+		{
+			Name:      "sudo",
+			Version:   "1.8.27-1+deb10u1",
+			Ecosystem: lockfile.DebianEcosystem,
+			CompareAs: lockfile.DebianEcosystem,
+		},
+	})
+}
+
+func TestParseDpkgStatus_Shuffled(t *testing.T) {
+	t.Parallel()
+
+	packages, err := lockfile.ParseDpkgStatus("fixtures/dpkg/shuffled_status")
+
+	if err != nil {
+		t.Errorf("Got unexpected error: %v", err)
+	}
+
+	expectPackages(t, packages, []lockfile.PackageDetails{
+		{
+			Name:      "glibc",
+			Version:   "2.31-13+deb11u5",
+			Ecosystem: lockfile.DebianEcosystem,
+			CompareAs: lockfile.DebianEcosystem,
+		},
+	})
+}
+
+func TestParseDpkgStatus_Multiple(t *testing.T) {
+	t.Parallel()
+
+	packages, err := lockfile.ParseDpkgStatus("fixtures/dpkg/multiple_status")
+
+	if err != nil {
+		t.Errorf("Got unexpected error: %v", err)
+	}
+
+	expectPackages(t, packages, []lockfile.PackageDetails{
+		{
+			Name:      "bash",
+			Version:   "5.1-2+deb11u1",
+			Ecosystem: lockfile.DebianEcosystem,
+			CompareAs: lockfile.DebianEcosystem,
+		},
+		{
+			Name:      "util-linux",
+			Version:   "2.36.1-8+deb11u1",
+			Ecosystem: lockfile.DebianEcosystem,
+			CompareAs: lockfile.DebianEcosystem,
+		},
+		{
+			Name:      "glibc",
+			Version:   "2.31-13+deb11u5",
+			Ecosystem: lockfile.DebianEcosystem,
+			CompareAs: lockfile.DebianEcosystem,
+		},
+	})
+}
+
+func TestParseDpkgStatus_Source_Ver_Override(t *testing.T) {
+	t.Parallel()
+
+	packages, err := lockfile.ParseDpkgStatus("fixtures/dpkg/source_ver_override_status")
+
+	if err != nil {
+		t.Errorf("Got unexpected error: %v", err)
+	}
+
+	expectPackages(t, packages, []lockfile.PackageDetails{
+		{
+			Name:      "lvm2",
+			Version:   "2.02.176-4.1ubuntu3",
+			Ecosystem: lockfile.DebianEcosystem,
+			CompareAs: lockfile.DebianEcosystem,
+		},
+	})
+}
diff --git a/pkg/lockfile/parse-maven-lock_test.go b/pkg/lockfile/parse-maven-lock_test.go
index b838721..718c05f 100644
--- a/pkg/lockfile/parse-maven-lock_test.go
+++ b/pkg/lockfile/parse-maven-lock_test.go
@@ -79,6 +79,37 @@ func TestParseMavenLock_TwoPackages(t *testing.T) {
 	})
 }
 
+func TestParseMavenLock_WithDependencyManagement(t *testing.T) {
+	t.Parallel()
+
+	packages, err := lockfile.ParseMavenLock("fixtures/maven/with-dependency-management.xml")
+
+	if err != nil {
+		t.Errorf("Got unexpected error: %v", err)
+	}
+
+	expectPackages(t, packages, []lockfile.PackageDetails{
+		{
+			Name:      "io.netty:netty-all",
+			Version:   "4.1.42.Final",
+			Ecosystem: lockfile.MavenEcosystem,
+			CompareAs: lockfile.MavenEcosystem,
+		},
+		{
+			Name:      "org.slf4j:slf4j-log4j12",
+			Version:   "1.7.25",
+			Ecosystem: lockfile.MavenEcosystem,
+			CompareAs: lockfile.MavenEcosystem,
+		},
+		{
+			Name:      "com.google.code.findbugs:jsr305",
+			Version:   "3.0.2",
+			Ecosystem: lockfile.MavenEcosystem,
+			CompareAs: lockfile.MavenEcosystem,
+		},
+	})
+}
+
 func TestParseMavenLock_Interpolation(t *testing.T) {
 	t.Parallel()