Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package grype-db for openSUSE:Factory 
checked in at 2025-10-24 17:23:40
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/grype-db (Old)
 and      /work/SRC/openSUSE:Factory/.grype-db.new.1980 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "grype-db"

Fri Oct 24 17:23:40 2025 rev:20 rq:1313312 version:0.46.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/grype-db/grype-db.changes        2025-10-17 
17:27:13.029618302 +0200
+++ /work/SRC/openSUSE:Factory/.grype-db.new.1980/grype-db.changes      
2025-10-24 17:24:30.546124998 +0200
@@ -1,0 +2,21 @@
+Fri Oct 24 05:08:16 UTC 2025 - Johannes Kastl 
<[email protected]>
+
+- Update to version 0.46.0:
+  * Added Features
+    - enable emitting unaffected packages for AlmaLinux RPMs from
+      Alma OSV data [#686 @willmurphyscode]
+  * Additional Changes
+    - remove io.ReadAll in tarutils [#718 @willmurphyscode]
+    - Switch to using runs-on [#720 @wagoodman]
+  * Dependencies
+    - chore(deps): update anchore dependencies (#725)
+    - chore(deps): Bump astral-sh/setup-uv in
+      /.github/actions/bootstrap (#721)
+    - chore(deps): Bump github.com/klauspost/compress from 1.18.0
+      to 1.18.1 (#722)
+    - chore(deps): update tools to latest versions (#724)
+    - chore(deps): Bump github.com/anchore/grype from 0.101.0 to
+      0.101.1 (#717)
+    - chore(deps): update tools to latest versions (#715)
+
+-------------------------------------------------------------------

Old:
----
  grype-db-0.45.0.obscpio

New:
----
  grype-db-0.46.0.obscpio

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ grype-db.spec ++++++
--- /var/tmp/diff_new_pack.WvbeXv/_old  2025-10-24 17:24:32.274197668 +0200
+++ /var/tmp/diff_new_pack.WvbeXv/_new  2025-10-24 17:24:32.274197668 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           grype-db
-Version:        0.45.0
+Version:        0.46.0
 Release:        0
 Summary:        A vulnerability scanner for container images and filesystems
 License:        Apache-2.0

++++++ _service ++++++
--- /var/tmp/diff_new_pack.WvbeXv/_old  2025-10-24 17:24:32.326199855 +0200
+++ /var/tmp/diff_new_pack.WvbeXv/_new  2025-10-24 17:24:32.330200023 +0200
@@ -3,7 +3,7 @@
     <param name="url">https://github.com/anchore/grype-db</param>
     <param name="scm">git</param>
     <param name="exclude">.git</param>
-    <param name="revision">v0.45.0</param>
+    <param name="revision">v0.46.0</param>
     <param name="versionformat">@PARENT_TAG@</param>
     <param name="versionrewrite-pattern">v(.*)</param>
     <param name="changesgenerate">enable</param>

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.WvbeXv/_old  2025-10-24 17:24:32.374201874 +0200
+++ /var/tmp/diff_new_pack.WvbeXv/_new  2025-10-24 17:24:32.378202042 +0200
@@ -1,6 +1,6 @@
 <servicedata>
 <service name="tar_scm">
                 <param name="url">https://github.com/anchore/grype-db</param>
-              <param 
name="changesrevision">342891aa516b3f52bd4e04ad052be454c261cdc0</param></service></servicedata>
+              <param 
name="changesrevision">127e29f2e1331d0368bed7b241c8f19069ecee54</param></service></servicedata>
 (No newline at EOF)
 

++++++ grype-db-0.45.0.obscpio -> grype-db-0.46.0.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/grype-db-0.45.0/.binny.yaml 
new/grype-db-0.46.0/.binny.yaml
--- old/grype-db-0.45.0/.binny.yaml     2025-10-15 19:59:51.000000000 +0200
+++ new/grype-db-0.46.0/.binny.yaml     2025-10-23 13:33:17.000000000 +0200
@@ -26,7 +26,7 @@
   # used to release all artifacts
   - name: goreleaser
     version:
-      want: v2.12.5
+      want: v2.12.6
     method: github-release
     with:
       repo: goreleaser/goreleaser
@@ -58,7 +58,7 @@
   # used for triggering a release
   - name: gh
     version:
-      want: v2.81.0
+      want: v2.82.1
     method: github-release
     with:
       repo: cli/cli
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/grype-db-0.45.0/data/vulnerability-match-labels/labels/docker.io+centos@sha256:3688aa867eb84332460e172b9250c9c198fdfd8d987605fd53f246f498c60bcf/f91f8814-9cdc-4325-8ae6-eb87305ff29b.json
 
new/grype-db-0.46.0/data/vulnerability-match-labels/labels/docker.io+centos@sha256:3688aa867eb84332460e172b9250c9c198fdfd8d987605fd53f246f498c60bcf/f91f8814-9cdc-4325-8ae6-eb87305ff29b.json
--- 
old/grype-db-0.45.0/data/vulnerability-match-labels/labels/docker.io+centos@sha256:3688aa867eb84332460e172b9250c9c198fdfd8d987605fd53f246f498c60bcf/f91f8814-9cdc-4325-8ae6-eb87305ff29b.json
       2025-10-15 19:59:51.000000000 +0200
+++ 
new/grype-db-0.46.0/data/vulnerability-match-labels/labels/docker.io+centos@sha256:3688aa867eb84332460e172b9250c9c198fdfd8d987605fd53f246f498c60bcf/f91f8814-9cdc-4325-8ae6-eb87305ff29b.json
       2025-10-23 13:33:17.000000000 +0200
@@ -1 +1 @@
-{"ID": "f91f8814-9cdc-4325-8ae6-eb87305ff29b", "effective_cve": 
"CVE-2016-2183", "image": {"exact": 
"docker.io/centos@sha256:3688aa867eb84332460e172b9250c9c198fdfd8d987605fd53f246f498c60bcf"},
 "label": "TP", "package": {"name": "openssl", "version": "1.0.1e-57.el6"}, 
"timestamp": "2022-09-15T18:56:49-04:00", "user": "wagoodman", 
"vulnerability_id": "CVE-2016-2183"}
\ No newline at end of file
+{"ID": "f91f8814-9cdc-4325-8ae6-eb87305ff29b", "effective_cve": 
"CVE-2016-2183", "image": {"exact": 
"docker.io/centos@sha256:3688aa867eb84332460e172b9250c9c198fdfd8d987605fd53f246f498c60bcf"},
 "label": "FP", "package": {"name": "openssl", "version": "1.0.1e-57.el6"}, 
"timestamp": "2022-09-15T18:56:49-04:00", "user": "wagoodman", 
"vulnerability_id": "CVE-2016-2183"}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/grype-db-0.45.0/go.mod new/grype-db-0.46.0/go.mod
--- old/grype-db-0.45.0/go.mod  2025-10-15 19:59:51.000000000 +0200
+++ new/grype-db-0.46.0/go.mod  2025-10-23 13:33:17.000000000 +0200
@@ -8,9 +8,9 @@
        github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
        github.com/adrg/xdg v0.5.3
        github.com/anchore/go-logger v0.0.0-20250318195838-07ae343dd722
-       github.com/anchore/grype v0.101.0
+       github.com/anchore/grype v0.102.0
        github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115
-       github.com/anchore/syft v1.34.1
+       github.com/anchore/syft v1.36.0
        github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de
        github.com/dave/jennifer v1.7.1
        github.com/dustin/go-humanize v1.0.1
@@ -26,7 +26,7 @@
        github.com/hashicorp/go-multierror v1.1.1
        github.com/iancoleman/strcase v0.3.0
        github.com/jinzhu/copier v0.4.0
-       github.com/klauspost/compress v1.18.0
+       github.com/klauspost/compress v1.18.1
        github.com/mitchellh/go-homedir v1.1.0
        github.com/mitchellh/mapstructure v1.5.0
        github.com/openvex/go-vex v0.2.7
@@ -78,7 +78,7 @@
        github.com/anchore/archiver/v3 v3.5.3-0.20241210171143-5b1d8d1c7c51 // 
indirect
        github.com/anchore/clio v0.0.0-20250715152405-a0fa658e5084 // indirect
        github.com/anchore/fangs v0.0.0-20250716230140-94c22408c232 // indirect
-       github.com/anchore/go-collections v0.0.0-20241211140901-567f400e9a46 // 
indirect
+       github.com/anchore/go-collections v0.0.0-20251016125210-a3c352120e8c // 
indirect
        github.com/anchore/go-homedir v0.0.0-20250319154043-c29668562e4d // 
indirect
        github.com/anchore/go-lzo v0.1.0 // indirect
        github.com/anchore/go-macholibre v0.0.0-20250320151634-807da7ad2331 // 
indirect
@@ -162,7 +162,7 @@
        github.com/felixge/httpsnoop v1.0.4 // indirect
        github.com/fsnotify/fsnotify v1.9.0 // indirect
        github.com/gabriel-vasile/mimetype v1.4.10 // indirect
-       github.com/github/go-spdx/v2 v2.3.3 // indirect
+       github.com/github/go-spdx/v2 v2.3.4 // indirect
        github.com/glebarez/go-sqlite v1.22.0 // indirect
        github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
        github.com/go-git/go-billy/v5 v5.6.2 // indirect
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/grype-db-0.45.0/go.sum new/grype-db-0.46.0/go.sum
--- old/grype-db-0.45.0/go.sum  2025-10-15 19:59:51.000000000 +0200
+++ new/grype-db-0.46.0/go.sum  2025-10-23 13:33:17.000000000 +0200
@@ -134,8 +134,8 @@
 github.com/anchore/clio v0.0.0-20250715152405-a0fa658e5084/go.mod 
h1:42dWox8z4//b898OIELsQnSdYq9q1aCXkwp5fKF+BEU=
 github.com/anchore/fangs v0.0.0-20250716230140-94c22408c232 
h1:aVC6r9h5wGNh8BYTW3CXxOdPoZzY/bBRWne1NvSTlO8=
 github.com/anchore/fangs v0.0.0-20250716230140-94c22408c232/go.mod 
h1:Zees1AEKNpXIRgdVAMYWITncarLFiPOtEQ7rl45V/h0=
-github.com/anchore/go-collections v0.0.0-20241211140901-567f400e9a46 
h1:huvprHsfzhrIIkk7kja1Fm5Wn3mnwPv4CeHrGlGD3ds=
-github.com/anchore/go-collections v0.0.0-20241211140901-567f400e9a46/go.mod 
h1:1aiktV46ATCkuVg0O573ZrH56BUawTECPETbZyBcqT8=
+github.com/anchore/go-collections v0.0.0-20251016125210-a3c352120e8c 
h1:eoJXyC0n7DZ4YvySG/ETdYkTar2Due7eH+UmLK6FbrA=
+github.com/anchore/go-collections v0.0.0-20251016125210-a3c352120e8c/go.mod 
h1:1aiktV46ATCkuVg0O573ZrH56BUawTECPETbZyBcqT8=
 github.com/anchore/go-homedir v0.0.0-20250319154043-c29668562e4d 
h1:gT69osH9AsdpOfqxbRwtxcNnSZ1zg4aKy2BevO3ZBdc=
 github.com/anchore/go-homedir v0.0.0-20250319154043-c29668562e4d/go.mod 
h1:PhSnuFYknwPZkOWKB1jXBNToChBA+l0FjwOxtViIc50=
 github.com/anchore/go-logger v0.0.0-20250318195838-07ae343dd722 
h1:2SqmFgE7h+Ql4VyBzhjLkRF/3gDrcpUBj8LjvvO6OOM=
@@ -155,14 +155,14 @@
 github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04/go.mod 
h1:6dK64g27Qi1qGQZ67gFmBFvEHScy0/C8qhQhNe5B5pQ=
 github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 
h1:rmZG77uXgE+o2gozGEBoUMpX27lsku+xrMwlmBZJtbg=
 github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4/go.mod 
h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
-github.com/anchore/grype v0.101.0 
h1:8b+Oj0BZLNSBMhryWckSjGKdvqVx2NNYTM+pnbL3UkA=
-github.com/anchore/grype v0.101.0/go.mod 
h1:+PcBoFeG0beVdTPbMUNIxkNdQU0IR1FUXOXdpX2peuM=
+github.com/anchore/grype v0.102.0 
h1:yf8YKGklukxIobObK2WQEYEp8mU3ofTnB4+cBR7S9vE=
+github.com/anchore/grype v0.102.0/go.mod 
h1:9EFzjrlx81aP37YKpxktXSbAhgJ8jNCm4jP9tim9jww=
 github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 
h1:ZyRCmiEjnoGJZ1+Ah0ZZ/mKKqNhGcUZBl0s7PTTDzvY=
 github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115/go.mod 
h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI=
 github.com/anchore/stereoscope v0.1.11 
h1:YP/XUNcJyMbOOPAWPkeZNCVlKKTRO2cnBTEeUW6I40Y=
 github.com/anchore/stereoscope v0.1.11/go.mod 
h1:G3PZlzPbxFhylj9pQwtqfVPaahuWmy/UCtv5FTIIMvg=
-github.com/anchore/syft v1.34.1 h1:OdM9guARidtMPBL6ju83vV/GauZ6Tb6UwhFlLyLHbNw=
-github.com/anchore/syft v1.34.1/go.mod 
h1:J9fOxYe2o9I5sML6ntNF2uiPYZ+vwcWVPM26tCSyf3M=
+github.com/anchore/syft v1.36.0 h1:vmrQz/eCPEdniHi2XRqEXxpvO3Q3wHL9o+YcE45XtUI=
+github.com/anchore/syft v1.36.0/go.mod 
h1:DdJMDHhI2V7pOjC/5FL98BKbG2DkbIT5zYmig6AORdU=
 github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod 
h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
 github.com/andybalholm/brotli v1.2.0 
h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
 github.com/andybalholm/brotli v1.2.0/go.mod 
h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
@@ -415,8 +415,8 @@
 github.com/gabriel-vasile/mimetype v1.4.10 
h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0=
 github.com/gabriel-vasile/mimetype v1.4.10/go.mod 
h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
 github.com/ghodss/yaml v1.0.0/go.mod 
h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/github/go-spdx/v2 v2.3.3 
h1:QI7evnHWEfWkT54eJwkoV/f3a0xD3gLlnVmT5wQG6LE=
-github.com/github/go-spdx/v2 v2.3.3/go.mod 
h1:2ZxKsOhvBp+OYBDlsGnUMcchLeo2mrpEBn2L1C+U3IQ=
+github.com/github/go-spdx/v2 v2.3.4 
h1:6VNAsYWvQge+SOeoubTlH81MY21d5uekXNIRGfXMNXo=
+github.com/github/go-spdx/v2 v2.3.4/go.mod 
h1:7LYNCshU2Gj17qZ0heJ5CQUKWWmpd98K7o93K8fJSMk=
 github.com/gkampitakis/ciinfo v0.3.2 
h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs=
 github.com/gkampitakis/ciinfo v0.3.2/go.mod 
h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
 github.com/gkampitakis/go-diff v1.3.2 
h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M=
@@ -667,8 +667,8 @@
 github.com/kisielk/errcheck v1.5.0/go.mod 
h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
 github.com/kisielk/gotool v1.0.0/go.mod 
h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 github.com/klauspost/compress v1.4.1/go.mod 
h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
-github.com/klauspost/compress v1.18.0 
h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
-github.com/klauspost/compress v1.18.0/go.mod 
h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
+github.com/klauspost/compress v1.18.1 
h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co=
+github.com/klauspost/compress v1.18.1/go.mod 
h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0=
 github.com/klauspost/cpuid v1.2.0/go.mod 
h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
 github.com/klauspost/pgzip v1.2.6 
h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
 github.com/klauspost/pgzip v1.2.6/go.mod 
h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/grype-db-0.45.0/internal/tarutil/reader_entry.go 
new/grype-db-0.46.0/internal/tarutil/reader_entry.go
--- old/grype-db-0.45.0/internal/tarutil/reader_entry.go        2025-10-15 
19:59:51.000000000 +0200
+++ new/grype-db-0.46.0/internal/tarutil/reader_entry.go        2025-10-23 
13:33:17.000000000 +0200
@@ -3,6 +3,7 @@
 import (
        "archive/tar"
        "bytes"
+       "fmt"
        "io"
        "os"
 
@@ -32,6 +33,65 @@
        })
 }
 
+// autoDeleteFile wraps an *os.File and deletes it when closed.
+type autoDeleteFile struct {
+       *os.File
+}
+
+func (f *autoDeleteFile) Close() error {
+       name := f.Name()
+       err := f.File.Close()
+       if removeErr := os.Remove(name); removeErr != nil && err == nil {
+               err = removeErr
+       }
+       return err
+}
+
+// readerWithSize determines the size of the reader's content without reading 
the entire content into memory.
+// For known reader types (bytes.Reader, os.File), it queries the size 
directly.
+// For unknown types, it copies to a temp file to avoid loading into memory.
+// Returns the size, a ReadCloser for the content (may be different from 
input), and any error.
+func readerWithSize(reader io.Reader) (int64, io.ReadCloser, error) {
+       switch r := reader.(type) {
+       case *bytes.Reader:
+               // For bytes.Reader (used by NewEntryFromBytes), get actual size
+               return r.Size(), io.NopCloser(reader), nil
+       case interface{ Stat() (os.FileInfo, error) }:
+               // For *os.File, use Stat to get size
+               stat, err := r.Stat()
+               if err != nil {
+                       return 0, nil, err
+               }
+               // Check if it's already a ReadCloser
+               if rc, ok := reader.(io.ReadCloser); ok {
+                       return stat.Size(), rc, nil
+               }
+               return 0, nil, fmt.Errorf("reader with Stat() must implement 
io.ReadCloser")
+       default:
+               // Fallback for unknown reader types: copy to temp file to 
avoid loading into memory
+               tmpFile, err := os.CreateTemp("", "grype-db-tar-*")
+               if err != nil {
+                       return 0, nil, fmt.Errorf("unable to create temp file: 
%w", err)
+               }
+
+               size, err := io.Copy(tmpFile, reader)
+               if err != nil {
+                       tmpFile.Close()
+                       os.Remove(tmpFile.Name())
+                       return 0, nil, fmt.Errorf("unable to copy to temp file: 
%w", err)
+               }
+
+               // Seek back to beginning for reading
+               if _, err := tmpFile.Seek(0, 0); err != nil {
+                       tmpFile.Close()
+                       os.Remove(tmpFile.Name())
+                       return 0, nil, fmt.Errorf("unable to seek temp file: 
%w", err)
+               }
+
+               return size, &autoDeleteFile{File: tmpFile}, nil
+       }
+}
+
 func writeEntry(tw lowLevelWriter, filename string, fileInfo os.FileInfo, 
opener func() (io.Reader, error)) error {
        log.WithFields("path", filename).Trace("adding file to archive")
 
@@ -69,17 +129,20 @@
                        return err
                }
 
-               contents, err := io.ReadAll(reader)
+               size, readCloser, err := readerWithSize(reader)
                if err != nil {
                        return err
                }
-               header.Size = int64(len(contents))
+               defer readCloser.Close()
+
+               header.Size = size
 
                if err := tw.WriteHeader(header); err != nil {
                        return err
                }
 
-               if _, err := tw.Write(contents); err != nil {
+               // Stream the file contents directly to the tar writer
+               if _, err := io.Copy(tw, readCloser); err != nil {
                        return err
                }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/grype-db-0.45.0/internal/tarutil/reader_entry_test.go 
new/grype-db-0.46.0/internal/tarutil/reader_entry_test.go
--- old/grype-db-0.45.0/internal/tarutil/reader_entry_test.go   2025-10-15 
19:59:51.000000000 +0200
+++ new/grype-db-0.46.0/internal/tarutil/reader_entry_test.go   2025-10-23 
13:33:17.000000000 +0200
@@ -2,9 +2,12 @@
 
 import (
        "archive/tar"
+       "bytes"
+       "io"
        "io/fs"
        "os"
        "path/filepath"
+       "strings"
        "testing"
        "time"
 
@@ -141,3 +144,97 @@
                })
        }
 }
+
+func Test_readerWithSize(t *testing.T) {
+       testData := "hello world from test"
+
+       tests := []struct {
+               name     string
+               reader   func(t *testing.T) io.Reader
+               wantSize int64
+               wantErr  require.ErrorAssertionFunc
+       }{
+               {
+                       name: "bytes.Reader",
+                       reader: func(t *testing.T) io.Reader {
+                               return bytes.NewReader([]byte(testData))
+                       },
+                       wantSize: int64(len(testData)),
+               },
+               {
+                       name: "os.File success",
+                       reader: func(t *testing.T) io.Reader {
+                               dir := t.TempDir()
+                               path := filepath.Join(dir, "test.txt")
+                               require.NoError(t, os.WriteFile(path, 
[]byte(testData), 0644))
+                               f, err := os.Open(path)
+                               require.NoError(t, err)
+                               t.Cleanup(func() { f.Close() })
+                               return f
+                       },
+                       wantSize: int64(len(testData)),
+               },
+               {
+                       name: "os.File stat fails",
+                       reader: func(t *testing.T) io.Reader {
+                               dir := t.TempDir()
+                               path := filepath.Join(dir, "test.txt")
+                               require.NoError(t, os.WriteFile(path, 
[]byte(testData), 0644))
+                               f, err := os.Open(path)
+                               require.NoError(t, err)
+                               f.Close()
+                               return f
+                       },
+                       wantErr: require.Error,
+               },
+               {
+                       name: "unknown reader creates temp file",
+                       reader: func(t *testing.T) io.Reader {
+                               return strings.NewReader(testData)
+                       },
+                       wantSize: int64(len(testData)),
+               },
+       }
+
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       if tt.wantErr == nil {
+                               tt.wantErr = require.NoError
+                       }
+
+                       reader := tt.reader(t)
+                       size, rc, err := readerWithSize(reader)
+                       tt.wantErr(t, err)
+                       if err != nil {
+                               return
+                       }
+                       defer rc.Close()
+
+                       assert.Equal(t, tt.wantSize, size)
+
+                       content, err := io.ReadAll(rc)
+                       require.NoError(t, err)
+                       assert.Equal(t, testData, string(content))
+               })
+       }
+}
+
+func Test_autoDeleteFile(t *testing.T) {
+       dir := t.TempDir()
+       path := filepath.Join(dir, "test.txt")
+       require.NoError(t, os.WriteFile(path, []byte("test content"), 0644))
+
+       f, err := os.Open(path)
+       require.NoError(t, err)
+
+       adf := &autoDeleteFile{File: f}
+
+       _, err = os.Stat(path)
+       require.NoError(t, err)
+
+       err = adf.Close()
+       require.NoError(t, err)
+
+       _, err = os.Stat(path)
+       assert.True(t, os.IsNotExist(err))
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/grype-db-0.45.0/pkg/process/v6/transformers/osv/test-fixtures/ALSA-2025-7467.json
 
new/grype-db-0.46.0/pkg/process/v6/transformers/osv/test-fixtures/ALSA-2025-7467.json
--- 
old/grype-db-0.45.0/pkg/process/v6/transformers/osv/test-fixtures/ALSA-2025-7467.json
       1970-01-01 01:00:00.000000000 +0100
+++ 
new/grype-db-0.46.0/pkg/process/v6/transformers/osv/test-fixtures/ALSA-2025-7467.json
       2025-10-23 13:33:17.000000000 +0200
@@ -0,0 +1,61 @@
+{
+  "id": "ALSA-2025:7467",
+  "summary": "Moderate: skopeo security update",
+  "aliases": [
+    "CVE-2025-27144"
+  ],
+  "affected": [
+    {
+      "package": {
+        "ecosystem": "AlmaLinux:10",
+        "name": "skopeo"
+      },
+      "ranges": [
+        {
+          "type": "ECOSYSTEM",
+          "events": [
+            {
+              "introduced": "0"
+            },
+            {
+              "fixed": "2:1.18.1-1.el10_0"
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "package": {
+        "ecosystem": "AlmaLinux:10",
+        "name": "skopeo-tests"
+      },
+      "ranges": [
+        {
+          "type": "ECOSYSTEM",
+          "events": [
+            {
+              "introduced": "0"
+            },
+            {
+              "fixed": "2:1.18.1-1.el10_0"
+            }
+          ]
+        }
+      ]
+    }
+  ],
+  "published": "2025-05-13T00:00:00Z",
+  "modified": "2025-07-02T12:50:06Z",
+  "details": "The skopeo command lets you inspect images from container image 
registries.",
+  "references": [
+    {
+      "url": "https://errata.almalinux.org/10/ALSA-2025-7467.html";,
+      "type": "ADVISORY"
+    }
+  ],
+  "database_specific": {
+    "anchore": {
+      "record_type": "advisory"
+    }
+  }
+}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/grype-db-0.45.0/pkg/process/v6/transformers/osv/transform.go 
new/grype-db-0.46.0/pkg/process/v6/transformers/osv/transform.go
--- old/grype-db-0.45.0/pkg/process/v6/transformers/osv/transform.go    
2025-10-15 19:59:51.000000000 +0200
+++ new/grype-db-0.46.0/pkg/process/v6/transformers/osv/transform.go    
2025-10-23 13:33:17.000000000 +0200
@@ -4,11 +4,13 @@
        "fmt"
        "regexp"
        "sort"
+       "strconv"
        "strings"
 
        "github.com/google/osv-scanner/pkg/models"
 
        "github.com/anchore/grype-db/pkg/data"
+       "github.com/anchore/grype-db/pkg/process/internal/codename"
        "github.com/anchore/grype-db/pkg/process/internal/common"
        "github.com/anchore/grype-db/pkg/process/v6/transformers"
        "github.com/anchore/grype-db/pkg/process/v6/transformers/internal"
@@ -19,12 +21,23 @@
        "github.com/anchore/syft/syft/pkg"
 )
 
+const (
+       almaLinux = "almalinux"
+)
+
 func Transform(vulnerability unmarshal.OSVVulnerability, state provider.State) 
([]data.Entry, error) {
        severities, err := getSeverities(vulnerability)
        if err != nil {
                return nil, fmt.Errorf("unable to obtain severities: %w", err)
        }
 
+       isAdvisory := isAdvisoryRecord(vulnerability)
+       aliases := vulnerability.Aliases
+
+       if isAdvisory {
+               aliases = append(aliases, vulnerability.Related...)
+       }
+
        in := []any{
                grypeDB.VulnerabilityHandle{
                        Name:          vulnerability.ID,
@@ -38,14 +51,23 @@
                                Assigners:   nil,
                                Description: vulnerability.Details,
                                References:  getReferences(vulnerability),
-                               Aliases:     vulnerability.Aliases,
+                               Aliases:     aliases,
                                Severities:  severities,
                        },
                },
        }
 
-       for _, a := range getAffectedPackages(vulnerability) {
-               in = append(in, a)
+       // Check if this is an advisory record
+       if isAdvisory {
+               // For advisory records, emit unaffected packages
+               for _, u := range getUnaffectedPackages(vulnerability) {
+                       in = append(in, u)
+               }
+       } else {
+               // For vulnerability records, emit affected packages
+               for _, a := range getAffectedPackages(vulnerability) {
+                       in = append(in, a)
+               }
        }
 
        return transformers.NewEntries(in...), nil
@@ -67,19 +89,20 @@
        var aphs []grypeDB.AffectedPackageHandle
        for _, affected := range vuln.Affected {
                aph := grypeDB.AffectedPackageHandle{
-                       Package:   getPackage(affected.Package),
-                       BlobValue: &grypeDB.PackageBlob{CVEs: vuln.Aliases},
+                       Package:         getPackage(affected.Package),
+                       OperatingSystem: 
getOperatingSystemFromEcosystem(string(affected.Package.Ecosystem)),
+                       BlobValue:       &grypeDB.PackageBlob{CVEs: 
vuln.Aliases},
                }
 
-               if withCPE {
-                       aph.BlobValue.Qualifiers = &grypeDB.PackageQualifiers{
-                               PlatformCPEs: cpes.([]string),
-                       }
+               // Extract qualifiers (CPE and RPM modularity)
+               qualifiers := getPackageQualifiers(affected, cpes, withCPE)
+               if qualifiers != nil {
+                       aph.BlobValue.Qualifiers = qualifiers
                }
 
                var ranges []grypeDB.Range
                for _, r := range affected.Ranges {
-                       ranges = append(ranges, getGrypeRangesFromRange(r)...)
+                       ranges = append(ranges, getGrypeRangesFromRange(r, 
string(affected.Package.Ecosystem))...)
                }
                aph.BlobValue.Ranges = ranges
                aphs = append(aphs, aph)
@@ -91,6 +114,49 @@
        return aphs
 }
 
+// getPackageQualifiers extracts package qualifiers from affected package data
+// including CPE information and RPM modularity
+func getPackageQualifiers(affected models.Affected, cpes any, withCPE bool) 
*grypeDB.PackageQualifiers {
+       var qualifiers *grypeDB.PackageQualifiers
+
+       // Handle CPE qualifiers (existing logic)
+       if withCPE {
+               qualifiers = &grypeDB.PackageQualifiers{
+                       PlatformCPEs: cpes.([]string),
+               }
+       }
+
+       // Extract RPM modularity from ecosystem_specific
+       rpmModularity := extractRpmModularity(affected)
+       if rpmModularity != "" {
+               if qualifiers == nil {
+                       qualifiers = &grypeDB.PackageQualifiers{}
+               }
+               qualifiers.RpmModularity = &rpmModularity
+       }
+
+       return qualifiers
+}
+
+// extractRpmModularity extracts RPM modularity information from affected 
package ecosystem_specific
+func extractRpmModularity(affected models.Affected) string {
+       if affected.EcosystemSpecific == nil {
+               return ""
+       }
+
+       rpmModularity, ok := affected.EcosystemSpecific["rpm_modularity"]
+       if !ok {
+               return ""
+       }
+
+       rpmModularityStr, ok := rpmModularity.(string)
+       if !ok {
+               return ""
+       }
+
+       return rpmModularityStr
+}
+
 // OSV supports flattered ranges, so both formats below are valid:
 // "ranges": [
 //
@@ -139,7 +205,7 @@
 //     }
 //
 // ]
-func getGrypeRangesFromRange(r models.Range) []grypeDB.Range { // nolint: 
gocognit
+func getGrypeRangesFromRange(r models.Range, ecosystem string) []grypeDB.Range 
{ // nolint: gocognit
        var ranges []grypeDB.Range
        if len(r.Events) == 0 {
                return nil
@@ -184,7 +250,7 @@
                }
        }
 
-       rangeType := normalizeRangeType(r.Type)
+       rangeType := normalizeRangeType(r.Type, ecosystem)
        for _, e := range r.Events {
                switch {
                case e.Introduced != "" && e.Introduced != "0":
@@ -234,7 +300,7 @@
 }
 
 func normalizeConstraint(constraint string, rangeType string) string {
-       if rangeType == "semver" {
+       if rangeType == "semver" || rangeType == "bitnami" {
                return common.EnforceSemVerConstraint(constraint)
        }
        return constraint
@@ -254,7 +320,12 @@
        }
 }
 
-func normalizeRangeType(t models.RangeType) string {
+func normalizeRangeType(t models.RangeType, ecosystem string) string {
+       // For Bitnami ecosystem, use "bitnami" format instead of "semver"
+       if ecosystem == "Bitnami" && t == models.RangeSemVer {
+               return "bitnami"
+       }
+
        switch t {
        case models.RangeSemVer, models.RangeEcosystem, models.RangeGit:
                return strings.ToLower(string(t))
@@ -264,17 +335,63 @@
 }
 
 func getPackage(p models.Package) *grypeDB.Package {
+       // Try to determine package type from ecosystem or PURL
+       var pkgType pkg.Type
+       var ecosystem string
+
+       if p.Purl != "" {
+               pkgType = pkg.TypeFromPURL(p.Purl)
+               ecosystem = string(p.Ecosystem)
+       } else {
+               pkgType = getPackageTypeFromEcosystem(string(p.Ecosystem))
+               // If we found a package type from OS ecosystem, use it; 
otherwise use original ecosystem
+               if pkgType != "" {
+                       ecosystem = string(pkgType)
+               } else {
+                       ecosystem = string(p.Ecosystem)
+               }
+       }
+
        return &grypeDB.Package{
-               Ecosystem: string(p.Ecosystem),
-               Name:      name.Normalize(p.Name, pkg.TypeFromPURL(p.Purl)),
+               Ecosystem: ecosystem,
+               Name:      name.Normalize(p.Name, pkgType),
        }
 }
 
+// getPackageTypeFromEcosystem determines package type from OSV ecosystem
+// Currently only supports AlmaLinux; other ecosystems use PURL-based detection
+func getPackageTypeFromEcosystem(ecosystem string) pkg.Type {
+       if ecosystem == "" {
+               return ""
+       }
+
+       // Split ecosystem by colon to get OS name
+       parts := strings.Split(ecosystem, ":")
+       osName := strings.ToLower(parts[0])
+
+       // Only handle AlmaLinux
+       if osName == almaLinux {
+               return pkg.RpmPkg
+       }
+
+       // For other ecosystems (like Bitnami, npm, pypi, etc.), return empty 
type
+       // The package type will be determined from PURL if available
+       return ""
+}
+
 func getReferences(vuln unmarshal.OSVVulnerability) []grypeDB.Reference {
        var refs []grypeDB.Reference
        for _, ref := range vuln.References {
+               // For advisory references, use the vulnerability ID as the 
advisory ID
+               // This allows tools consuming the data to link back to the 
specific advisory
+               refID := ""
+               if ref.Type == models.ReferenceAdvisory && 
isAdvisoryRecord(vuln) {
+                       refID = vuln.ID
+               }
+
                refs = append(refs,
                        grypeDB.Reference{
+                               ID:   refID,
                                URL:  ref.URL,
                                Tags: []string{string(ref.Type)},
                        },
@@ -341,3 +458,232 @@
 
        return severities, nil
 }
+
+// getOperatingSystemFromEcosystem extracts operating system information from 
OSV ecosystem field
+// Currently only supports AlmaLinux ecosystems
+// Example: "AlmaLinux:8" -> almalinux 8
+func getOperatingSystemFromEcosystem(ecosystem string) 
*grypeDB.OperatingSystem {
+       if ecosystem == "" {
+               return nil
+       }
+
+       // Split ecosystem by colon to get components
+       parts := strings.Split(ecosystem, ":")
+       if len(parts) < 2 {
+               return nil
+       }
+
+       osName := strings.ToLower(parts[0])
+
+       // Only handle AlmaLinux
+       if osName != almaLinux {
+               return nil
+       }
+
+       osVersion := parts[1]
+
+       // Parse version into major/minor components
+       versionFields := strings.Split(osVersion, ".")
+       var majorVersion, minorVersion string
+       if len(versionFields) > 0 {
+               majorVersion = versionFields[0]
+               // Check if the first field is actually a number
+               if _, err := strconv.Atoi(majorVersion[0:1]); err != nil {
+                       // If not numeric, treat the whole thing as a label 
version
+                       return &grypeDB.OperatingSystem{
+                               Name:         normalizeOSName(osName),
+                               LabelVersion: osVersion,
+                               Codename:     
codename.LookupOS(normalizeOSName(osName), "", ""),
+                       }
+               }
+               if len(versionFields) > 1 {
+                       minorVersion = versionFields[1]
+               }
+       }
+
+       return &grypeDB.OperatingSystem{
+               Name:         normalizeOSName(osName),
+               MajorVersion: majorVersion,
+               MinorVersion: minorVersion,
+               Codename:     codename.LookupOS(normalizeOSName(osName), 
majorVersion, minorVersion),
+       }
+}
+
+// normalizeOSName normalizes operating system names for consistency
+// Currently only supports AlmaLinux
+func normalizeOSName(osName string) string {
+       osName = strings.ToLower(osName)
+
+       // Only handle AlmaLinux
+       if osName == almaLinux {
+               return almaLinux
+       }
+
+       return osName
+}
+
+// isAdvisoryRecord checks if the OSV record is marked as an advisory
+func isAdvisoryRecord(vuln unmarshal.OSVVulnerability) bool {
+       if vuln.DatabaseSpecific == nil {
+               return false
+       }
+
+       anchoreData, ok := vuln.DatabaseSpecific["anchore"]
+       if !ok {
+               return false
+       }
+
+       anchoreMap, ok := anchoreData.(map[string]any)
+       if !ok {
+               return false
+       }
+
+       recordType, ok := anchoreMap["record_type"]
+       if !ok {
+               return false
+       }
+
+       recordTypeStr, ok := recordType.(string)
+       if !ok {
+               return false
+       }
+
+       return recordTypeStr == "advisory"
+}
+
+// getUnaffectedPackages creates UnaffectedPackageHandle entries for advisory 
records
+func getUnaffectedPackages(vuln unmarshal.OSVVulnerability) 
[]grypeDB.UnaffectedPackageHandle {
+       if len(vuln.Affected) == 0 {
+               return nil
+       }
+
+       var uphs []grypeDB.UnaffectedPackageHandle
+       for _, affected := range vuln.Affected {
+               uph := grypeDB.UnaffectedPackageHandle{
+                       Package:         getPackage(affected.Package),
+                       OperatingSystem: 
getOperatingSystemFromEcosystem(string(affected.Package.Ecosystem)),
+                       BlobValue:       getUnaffectedBlob(vuln.Aliases, 
affected.Ranges, affected),
+               }
+               uphs = append(uphs, uph)
+       }
+
+       // stable ordering
+       sort.Sort(internal.ByUnaffectedPackage(uphs))
+
+       return uphs
+}
+
+// getUnaffectedBlob creates a package blob for unaffected packages 
(advisories)
+// For advisories, we need to invert the ranges to represent unaffected 
versions
+func getUnaffectedBlob(aliases []string, ranges []models.Range, affected 
models.Affected) *grypeDB.PackageBlob {
+       var grypeRanges []grypeDB.Range
+       ecosystem := string(affected.Package.Ecosystem)
+       for _, r := range ranges {
+               grypeRanges = append(grypeRanges, 
getGrypeUnaffectedRangesFromRange(r, ecosystem)...)
+       }
+
+       // Extract qualifiers including RPM modularity
+       qualifiers := getPackageQualifiers(affected, nil, false)
+
+       return &grypeDB.PackageBlob{
+               CVEs:       aliases,
+               Ranges:     grypeRanges,
+               Qualifiers: qualifiers,
+       }
+}
+
+// getGrypeUnaffectedRangesFromRange converts OSV ranges to unaffected version 
ranges for unaffected packages
+// This inverts the logic: instead of "< fix_version" (affected), we create 
">= fix_version" (unaffected)
+func getGrypeUnaffectedRangesFromRange(r models.Range, ecosystem string) 
[]grypeDB.Range {
+       if len(r.Events) == 0 {
+               return nil
+       }
+
+       fixByVersion := extractFixAvailability(r)
+       rangeType := normalizeRangeType(r.Type, ecosystem)
+
+       return buildUnaffectedRangesFromEvents(r.Events, fixByVersion, 
rangeType)
+}
+
+// extractFixAvailability extracts fix availability information from 
DatabaseSpecific
+func extractFixAvailability(r models.Range) map[string]grypeDB.FixAvailability 
{
+       fixByVersion := make(map[string]grypeDB.FixAvailability)
+
+       dbSpecific, hasDBSpecific := r.DatabaseSpecific["anchore"]
+       if !hasDBSpecific {
+               return fixByVersion
+       }
+
+       anchoreInfo, isMap := dbSpecific.(map[string]any)
+       if !isMap {
+               return fixByVersion
+       }
+
+       fixes, hasFixes := anchoreInfo["fixes"]
+       if !hasFixes {
+               return fixByVersion
+       }
+
+       fixList, isList := fixes.([]any)
+       if !isList {
+               return fixByVersion
+       }
+
+       for _, fixEntry := range fixList {
+               parseSingleFixEntry(fixEntry, fixByVersion)
+       }
+
+       return fixByVersion
+}
+
+// parseSingleFixEntry parses a single fix entry and adds it to the 
fixByVersion map
+func parseSingleFixEntry(fixEntry any, fixByVersion 
map[string]grypeDB.FixAvailability) {
+       fixMap, isMap := fixEntry.(map[string]any)
+       if !isMap {
+               return
+       }
+
+       version, vOk := fixMap["version"].(string)
+       kind, kOk := fixMap["kind"].(string)
+       date, dOk := fixMap["date"].(string)
+
+       if vOk && kOk && dOk {
+               fixByVersion[version] = grypeDB.FixAvailability{
+                       Date: internal.ParseTime(date),
+                       Kind: kind,
+               }
+       }
+}
+
+// buildUnaffectedRangesFromEvents processes events to create unaffected 
version ranges
+func buildUnaffectedRangesFromEvents(events []models.Event, fixByVersion 
map[string]grypeDB.FixAvailability, rangeType string) []grypeDB.Range {
+       var ranges []grypeDB.Range
+
+       for _, e := range events {
+               if e.Fixed != "" {
+                       unaffectedRange := createUnaffectedRange(e.Fixed, 
fixByVersion, rangeType)
+                       ranges = append(ranges, unaffectedRange)
+               }
+       }
+
+       return ranges
+}
+
+// createUnaffectedRange creates a single safe range for a fixed version
+func createUnaffectedRange(fixedVersion string, fixByVersion 
map[string]grypeDB.FixAvailability, rangeType string) grypeDB.Range {
+       var detail *grypeDB.FixDetail
+       if f, ok := fixByVersion[fixedVersion]; ok {
+               detail = &grypeDB.FixDetail{
+                       Available: &f,
+               }
+       }
+
+       constraint := fmt.Sprintf(">= %s", fixedVersion)
+       return grypeDB.Range{
+               Fix: normalizeFix(fixedVersion, detail),
+               Version: grypeDB.Version{
+                       Type:       rangeType,
+                       Constraint: normalizeConstraint(constraint, rangeType),
+               },
+       }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/grype-db-0.45.0/pkg/process/v6/transformers/osv/transform_test.go 
new/grype-db-0.46.0/pkg/process/v6/transformers/osv/transform_test.go
--- old/grype-db-0.45.0/pkg/process/v6/transformers/osv/transform_test.go       
2025-10-15 19:59:51.000000000 +0200
+++ new/grype-db-0.46.0/pkg/process/v6/transformers/osv/transform_test.go       
2025-10-23 13:33:17.000000000 +0200
@@ -68,6 +68,14 @@
        return r
 }
 
+func unaffectedPkgSlice(u ...grypeDB.UnaffectedPackageHandle) []any {
+       var r []any
+       for _, v := range u {
+               r = append(r, v)
+       }
+       return r
+}
+
 func TestTransform(t *testing.T) {
        tests := []struct {
                name        string
@@ -115,7 +123,7 @@
                                                        CVEs: 
[]string{"CVE-2020-11984"},
                                                        Ranges: 
[]grypeDB.Range{{
                                                                Version: 
grypeDB.Version{
-                                                                       Type:   
    "semver",
+                                                                       Type:   
    "bitnami",
                                                                        
Constraint: ">=2.4.32,<=2.4.43",
                                                                },
                                                        }},
@@ -165,7 +173,7 @@
                                                        CVEs: 
[]string{"CVE-2020-8201"},
                                                        Ranges: 
[]grypeDB.Range{{
                                                                Version: 
grypeDB.Version{
-                                                                       Type:   
    "semver",
+                                                                       Type:   
    "bitnami",
                                                                        
Constraint: ">=12.0.0,<12.18.4",
                                                                },
                                                                Fix: 
&grypeDB.Fix{
@@ -180,7 +188,7 @@
                                                                },
                                                        }, {
                                                                Version: 
grypeDB.Version{
-                                                                       Type:   
    "semver",
+                                                                       Type:   
    "bitnami",
                                                                        
Constraint: ">=14.0.0,<14.11.0",
                                                                },
                                                                Fix: 
&grypeDB.Fix{
@@ -199,6 +207,79 @@
                                ),
                        }},
                },
+               {
+                       name:        "AlmaLinux Advisory",
+                       fixturePath: "test-fixtures/ALSA-2025-7467.json",
+                       want: []transformers.RelatedEntries{{
+                               VulnerabilityHandle: 
&grypeDB.VulnerabilityHandle{
+                                       Name:          "ALSA-2025:7467",
+                                       Status:        
grypeDB.VulnerabilityActive,
+                                       ProviderID:    "osv",
+                                       Provider:      expectedProvider(),
+                                       ModifiedDate:  timeRef(time.Date(2025, 
time.July, 2, 12, 50, 6, 0, time.UTC)),
+                                       PublishedDate: timeRef(time.Date(2025, 
time.May, 13, 0, 0, 0, 0, time.UTC)),
+                                       BlobValue: &grypeDB.VulnerabilityBlob{
+                                               ID:          "ALSA-2025:7467",
+                                               Description: "The skopeo 
command lets you inspect images from container image registries.",
+                                               References: 
[]grypeDB.Reference{{
+                                                       ID:   "ALSA-2025:7467",
+                                                       URL:  
"https://errata.almalinux.org/10/ALSA-2025-7467.html";,
+                                                       Tags: 
[]string{"ADVISORY"},
+                                               }},
+                                               Aliases:    
[]string{"CVE-2025-27144"},
+                                               Severities: nil,
+                                       },
+                               },
+                               Related: unaffectedPkgSlice(
+                                       grypeDB.UnaffectedPackageHandle{
+                                               Package: &grypeDB.Package{
+                                                       Name:      "skopeo",
+                                                       Ecosystem: "rpm",
+                                               },
+                                               OperatingSystem: 
&grypeDB.OperatingSystem{
+                                                       Name:         
"almalinux",
+                                                       MajorVersion: "10",
+                                               },
+                                               BlobValue: &grypeDB.PackageBlob{
+                                                       CVEs: 
[]string{"CVE-2025-27144"},
+                                                       Ranges: 
[]grypeDB.Range{{
+                                                               Version: 
grypeDB.Version{
+                                                                       Type:   
    "ecosystem",
+                                                                       
Constraint: ">= 2:1.18.1-1.el10_0",
+                                                               },
+                                                               Fix: 
&grypeDB.Fix{
+                                                                       
Version: "2:1.18.1-1.el10_0",
+                                                                       State:  
 grypeDB.FixedStatus,
+                                                               },
+                                                       }},
+                                               },
+                                       },
+                                       grypeDB.UnaffectedPackageHandle{
+                                               Package: &grypeDB.Package{
+                                                       Name:      
"skopeo-tests",
+                                                       Ecosystem: "rpm",
+                                               },
+                                               OperatingSystem: 
&grypeDB.OperatingSystem{
+                                                       Name:         
"almalinux",
+                                                       MajorVersion: "10",
+                                               },
+                                               BlobValue: &grypeDB.PackageBlob{
+                                                       CVEs: 
[]string{"CVE-2025-27144"},
+                                                       Ranges: 
[]grypeDB.Range{{
+                                                               Version: 
grypeDB.Version{
+                                                                       Type:   
    "ecosystem",
+                                                                       
Constraint: ">= 2:1.18.1-1.el10_0",
+                                                               },
+                                                               Fix: 
&grypeDB.Fix{
+                                                                       
Version: "2:1.18.1-1.el10_0",
+                                                                       State:  
 grypeDB.FixedStatus,
+                                                               },
+                                                       }},
+                                               },
+                                       },
+                               ),
+                       }},
+               },
        }
        t.Parallel()
        for _, testToRun := range tests {
@@ -225,12 +306,14 @@
 }
 func Test_getGrypeRangesFromRange(t *testing.T) {
        tests := []struct {
-               name string
-               rnge models.Range
-               want []grypeDB.Range
+               name      string
+               rnge      models.Range
+               ecosystem string
+               want      []grypeDB.Range
        }{
                {
-                       name: "single range with 'fixed' status",
+                       name:      "single range with 'fixed' status",
+                       ecosystem: "npm",
                        rnge: models.Range{
                                Type: models.RangeSemVer,
                                Events: []models.Event{{
@@ -251,7 +334,8 @@
                        }},
                },
                {
-                       name: "single range with 'last affected' status",
+                       name:      "single range with 'last affected' status",
+                       ecosystem: "npm",
                        rnge: models.Range{
                                Type: models.RangeSemVer,
                                Events: []models.Event{{
@@ -268,7 +352,8 @@
                        }},
                },
                {
-                       name: "single range with no 'fixed' or 'last affected' 
status",
+                       name:      "single range with no 'fixed' or 'last 
affected' status",
+                       ecosystem: "npm",
                        rnge: models.Range{
                                Type: models.RangeSemVer,
                                Events: []models.Event{{
@@ -283,7 +368,8 @@
                        }},
                },
                {
-                       name: "single range introduced with '0'",
+                       name:      "single range introduced with '0'",
+                       ecosystem: "npm",
                        rnge: models.Range{
                                Type: models.RangeSemVer,
                                Events: []models.Event{{
@@ -300,7 +386,8 @@
                        }},
                },
                {
-                       name: "multiple ranges",
+                       name:      "multiple ranges",
+                       ecosystem: "npm",
                        rnge: models.Range{
                                Type: models.RangeSemVer,
                                Events: []models.Event{{
@@ -335,7 +422,8 @@
                        },
                },
                {
-                       name: "single range with database-specific fix 
availability",
+                       name:      "single range with database-specific fix 
availability",
+                       ecosystem: "npm",
                        rnge: models.Range{
                                Type: models.RangeSemVer,
                                Events: []models.Event{{
@@ -378,7 +466,7 @@
                test := testToRun
                t.Run(test.name, func(tt *testing.T) {
                        tt.Parallel()
-                       if got := getGrypeRangesFromRange(test.rnge); 
!reflect.DeepEqual(got, test.want) {
+                       if got := getGrypeRangesFromRange(test.rnge, 
test.ecosystem); !reflect.DeepEqual(got, test.want) {
                                t.Errorf("getGrypeRangesFromRange() = %v, want 
%v", got, test.want)
                        }
                })
@@ -500,3 +588,137 @@
                })
        }
 }
+
+func Test_extractRpmModularity(t *testing.T) {
+       tests := []struct {
+               name     string
+               affected models.Affected
+               want     string
+       }{
+               {
+                       name: "with rpm_modularity",
+                       affected: models.Affected{
+                               EcosystemSpecific: map[string]interface{}{
+                                       "rpm_modularity": "mariadb:10.3",
+                               },
+                       },
+                       want: "mariadb:10.3",
+               },
+               {
+                       name: "no ecosystem_specific",
+                       affected: models.Affected{
+                               EcosystemSpecific: nil,
+                       },
+                       want: "",
+               },
+               {
+                       name: "no rpm_modularity key",
+                       affected: models.Affected{
+                               EcosystemSpecific: map[string]interface{}{
+                                       "other_key": "some_value",
+                               },
+                       },
+                       want: "",
+               },
+               {
+                       name: "rpm_modularity not string",
+                       affected: models.Affected{
+                               EcosystemSpecific: map[string]interface{}{
+                                       "rpm_modularity": 123,
+                               },
+                       },
+                       want: "",
+               },
+               {
+                       name: "nodejs modularity",
+                       affected: models.Affected{
+                               EcosystemSpecific: map[string]interface{}{
+                                       "rpm_modularity": "nodejs:16",
+                               },
+                       },
+                       want: "nodejs:16",
+               },
+       }
+
+       for _, testToRun := range tests {
+               test := testToRun
+               t.Run(test.name, func(tt *testing.T) {
+                       got := extractRpmModularity(test.affected)
+                       if got != test.want {
+                               t.Errorf("extractRpmModularity() = %v, want 
%v", got, test.want)
+                       }
+               })
+       }
+}
+
+func Test_getPackageQualifiers(t *testing.T) {
+       tests := []struct {
+               name     string
+               affected models.Affected
+               cpes     any
+               withCPE  bool
+               want     *grypeDB.PackageQualifiers
+       }{
+               {
+                       name: "with rpm_modularity only",
+                       affected: models.Affected{
+                               EcosystemSpecific: map[string]interface{}{
+                                       "rpm_modularity": "mariadb:10.3",
+                               },
+                       },
+                       cpes:    nil,
+                       withCPE: false,
+                       want: &grypeDB.PackageQualifiers{
+                               RpmModularity: stringRef("mariadb:10.3"),
+                       },
+               },
+               {
+                       name: "with CPE only",
+                       affected: models.Affected{
+                               EcosystemSpecific: nil,
+                       },
+                       cpes:    
[]string{"cpe:2.3:a:vendor:product:*:*:*:*:*:*:*:*"},
+                       withCPE: true,
+                       want: &grypeDB.PackageQualifiers{
+                               PlatformCPEs: 
[]string{"cpe:2.3:a:vendor:product:*:*:*:*:*:*:*:*"},
+                       },
+               },
+               {
+                       name: "with both rpm_modularity and CPE",
+                       affected: models.Affected{
+                               EcosystemSpecific: map[string]interface{}{
+                                       "rpm_modularity": "nodejs:16",
+                               },
+                       },
+                       cpes:    
[]string{"cpe:2.3:a:nodejs:nodejs:*:*:*:*:*:*:*:*"},
+                       withCPE: true,
+                       want: &grypeDB.PackageQualifiers{
+                               PlatformCPEs:  
[]string{"cpe:2.3:a:nodejs:nodejs:*:*:*:*:*:*:*:*"},
+                               RpmModularity: stringRef("nodejs:16"),
+                       },
+               },
+               {
+                       name: "no qualifiers",
+                       affected: models.Affected{
+                               EcosystemSpecific: nil,
+                       },
+                       cpes:    nil,
+                       withCPE: false,
+                       want:    nil,
+               },
+       }
+
+       for _, testToRun := range tests {
+               test := testToRun
+               t.Run(test.name, func(tt *testing.T) {
+                       got := getPackageQualifiers(test.affected, test.cpes, 
test.withCPE)
+                       if !reflect.DeepEqual(got, test.want) {
+                               t.Errorf("getPackageQualifiers() = %v, want 
%v", got, test.want)
+                       }
+               })
+       }
+}
+
+func stringRef(s string) *string {
+       return &s
+}

++++++ grype-db.obsinfo ++++++
--- /var/tmp/diff_new_pack.WvbeXv/_old  2025-10-24 17:24:50.874979883 +0200
+++ /var/tmp/diff_new_pack.WvbeXv/_new  2025-10-24 17:24:50.882980220 +0200
@@ -1,5 +1,5 @@
 name: grype-db
-version: 0.45.0
-mtime: 1760551191
-commit: 342891aa516b3f52bd4e04ad052be454c261cdc0
+version: 0.46.0
+mtime: 1761219197
+commit: 127e29f2e1331d0368bed7b241c8f19069ecee54
 

++++++ vendor.tar.gz ++++++
/work/SRC/openSUSE:Factory/grype-db/vendor.tar.gz 
/work/SRC/openSUSE:Factory/.grype-db.new.1980/vendor.tar.gz differ: char 15, 
line 1

Reply via email to