Repository: aquasecurity/trivy (go)

Issue:
feat(alma): add AlmaLinux 10 support
## Description
AlmaLinux released https://wiki.almalinux.org/release-notes/#almalinux-os-10

We need to add support for this version.

### Required changes
- [x] Create PR for [vuln-list-update](https://github.com/aquasecurity/vuln-list-update/) (see [update#359](https://github.com/aquasecurity/vuln-list-update/pull/359))
  - Add `10` version in [AlmaReleaseVersion](https://github.com/aquasecurity/vuln-list-update/blob/6e704239a6518c28d703daa0bd6696694ae6ec62/alma/alma.go#L26)
  - Collect advisories for AlmaLinux (`make build` + `./vuln-list-update -vuln-list-dir ./temp -target alma`)
    - Recheck that advisories contain advisories for `10` version.
- [x] Build [trivy-db](https://github.com/aquasecurity/trivy-db) (see [update#359](https://github.com/aquasecurity/vuln-list-update/pull/359))
  - fetch all sources (`make db-fetch-vuln-list` + `make db-fetch-langs`) (you can use `CACHE_DIR` env).
  - overwrite AlmaLinux advisories (compiled in previous point)
  - build trivy-db (`make db-build` + `db-compact`)
  - open (you can use `boltwrowser` or [boltwiz](https://github.com/Moniseeta/boltwiz)) and recheck that trivy-db contains advisories for `10` version (you can add screenshot about this in `vuln-list-update` PR)
- [ ] Open PR for `Trivy`
  - add eol date for `10` version into [eolDates](https://github.com/aquasecurity/trivy/blob/c80310d7690d8aeb7d3d77416c18c0c8b9aebe17/pkg/detector/ospkg/alma/alma.go#L20)
  - check work with new trivy-db 
    - copy trivy-db from previous point
    - scan alma 10 image
    - confirm that Trivy detects vulnerability

### Discussed in https://github.com/aquasecurity/trivy/discussions/9188


Pull request description:
feat(alma): add AlmaLinux 10 support
## Description
This pr adds vul support for almalinux 10

Scanning AlmaLinux:10 image 

## Example
`almalinux:10` with manually installed `git-core-2.47.1-2.el9_6.aarch64.rpm `:
```bash
➜  ./trivy image almalinux:10-with-git-core 
2025-07-17T12:52:24+06:00       INFO    [vuln] Vulnerability scanning is enabled
2025-07-17T12:52:24+06:00       INFO    [secret] Secret scanning is enabled
2025-07-17T12:52:24+06:00       INFO    [secret] If your scanning is slow, please try '--scanners vuln' to disable secret scanning
2025-07-17T12:52:24+06:00       INFO    [secret] Please see also https://trivy.dev/dev/docs/scanner/secret#recommendation for faster secret detection
2025-07-17T12:52:24+06:00       INFO    Detected OS     family="alma" version="10.0"
2025-07-17T12:52:24+06:00       INFO    [alma] Detecting vulnerabilities...     os_version="10" pkg_num=143
2025-07-17T12:52:24+06:00       INFO    Number of language-specific files       num=0

Report Summary

┌────────────────────────────────────────┬──────┬─────────────────┬─────────┐
│                 Target                 │ Type │ Vulnerabilities │ Secrets │
├────────────────────────────────────────┼──────┼─────────────────┼─────────┤
│ almalinux:10-with-git-core (alma 10.0) │ alma │        1        │    -    │
└────────────────────────────────────────┴──────┴─────────────────┴─────────┘
Legend:
- '-': Not scanned
- '0': Clean (no security findings detected)


almalinux:10-with-git-core (alma 10.0)

Total: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 1, HIGH: 0, CRITICAL: 0)

┌──────────┬────────────────┬──────────┬────────┬───────────────────┬─────────────────┬───────────────────────────────────────────────────────┐
│ Library  │ Vulnerability  │ Severity │ Status │ Installed Version │  Fixed Version  │                         Title                         │
├──────────┼────────────────┼──────────┼────────┼───────────────────┼─────────────────┼───────────────────────────────────────────────────────┤
│ git-core │ CVE-2024-52005 │ MEDIUM   │ fixed  │ 2.47.1-2.el9_6    │ 2.47.1-2.el10_0 │ git: The sideband payload is passed unfiltered to the │
│          │                │          │        │                   │                 │ terminal in git...                                    │
│          │                │          │        │                   │                 │ https://avd.aquasec.com/nvd/cve-2024-52005            │
└──────────┴────────────────┴──────────┴────────┴───────────────────┴─────────────────┴───────────────────────────────────────────────────────┘

```

## Related issues
- Close #9189

## Related PRs
- https://github.com/aquasecurity/vuln-list-update/pull/359


## Checklist
- [x] I've read the [guidelines for contributing](https://trivy.dev/latest/community/contribute/pr/) to this repository.
- [x] I've followed the [conventions](https://trivy.dev/latest/community/contribute/pr/#title) in the PR title.
- [ ] I've added tests that prove my fix is effective or that my feature works.
- [x] I've updated the [documentation](https://github.com/aquasecurity/trivy/blob/main/docs) with the relevant information (if needed).
- [ ] I've added usage information (if the PR introduces new options)
- [ ] I've included a "before" and "after" example to the description (if the PR is a user interface change).


Patch:
diff --git a/docs/docs/coverage/os/index.md b/docs/docs/coverage/os/index.md
index 993725678..4af329177 100644
--- a/docs/docs/coverage/os/index.md
+++ b/docs/docs/coverage/os/index.md
@@ -18,7 +18,7 @@ Trivy supports operating systems for
 | [Red Hat Enterprise Linux](rhel.md)   | 6, 7, 8, 9                          | dnf/yum/rpm      |
 | [Red Hat Enterprise Linux](rhel.md)   | 10 (SBOM only)                      | dnf/yum/rpm      |
 | [CentOS](centos.md)[^1]               | 6, 7, 8                             | dnf/yum/rpm      |
-| [AlmaLinux](alma.md)                  | 8, 9                                | dnf/yum/rpm      |
+| [AlmaLinux](alma.md)                  | 8, 9, 10                            | dnf/yum/rpm      |
 | [Rocky Linux](rocky.md)               | 8, 9                                | dnf/yum/rpm      |
 | [Oracle Linux](oracle.md)             | 5, 6, 7, 8                          | dnf/yum/rpm      |
 | [Azure Linux (CBL-Mariner)](azure.md) | 1.0, 2.0, 3.0                       | tdnf/dnf/yum/rpm |
diff --git a/docs/docs/coverage/others/rootio.md b/docs/docs/coverage/others/rootio.md
index c47aae5c3..2d36a3339 100644
--- a/docs/docs/coverage/others/rootio.md
+++ b/docs/docs/coverage/others/rootio.md
@@ -13,6 +13,9 @@ Root.io patches are detected when Trivy finds packages with specific version suf
 When Root.io patches are detected, Trivy automatically switches to Root.io scanning mode for vulnerability detection.
 Even when the original OS distributor (Debian, Ubuntu, Alpine) has not provided a patch for a vulnerability, Trivy will display Root.io patches if they are available.
 
+!!! note
+    For vulnerabilities, Trivy uses the severity level from the original OS vendor (if the vendor has specified a severity).
+
 For detailed information about supported scanners, features, and functionality, please refer to the documentation for the underlying OS:
 
 - [Debian](../os/debian.md)
diff --git a/go.mod b/go.mod
index ffcd9f9c6..c5914888b 100644
--- a/go.mod
+++ b/go.mod
@@ -16,7 +16,7 @@ require (
 	github.com/apparentlymart/go-cidr v1.1.0
 	github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986
 	github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce
-	github.com/aquasecurity/go-npm-version v0.0.1
+	github.com/aquasecurity/go-npm-version v0.0.2
 	github.com/aquasecurity/go-pep440-version v0.0.1
 	github.com/aquasecurity/go-version v0.0.1
 	github.com/aquasecurity/iamgo v0.0.10
@@ -24,7 +24,7 @@ require (
 	github.com/aquasecurity/testdocker v0.0.0-20250616060700-ba6845ac6d17
 	github.com/aquasecurity/tml v0.6.1
 	github.com/aquasecurity/trivy-checks v1.11.3-0.20250604022615-9a7efa7c9169
-	github.com/aquasecurity/trivy-db v0.0.0-20250627124416-ca81c496a932
+	github.com/aquasecurity/trivy-db v0.0.0-20250716122853-45f09ec4df9c
 	github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48
 	github.com/aquasecurity/trivy-kubernetes v0.9.0
 	github.com/aws/aws-sdk-go-v2 v1.36.5
diff --git a/go.sum b/go.sum
index 64716fca0..a30366e26 100644
--- a/go.sum
+++ b/go.sum
@@ -810,8 +810,8 @@ github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986 h1:2a30
 github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986/go.mod h1:NT+jyeCzXk6vXR5MTkdn4z64TgGfE5HMLC8qfj5unl8=
 github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce h1:QgBRgJvtEOBtUXilDb1MLi1p1MWoyFDXAu5DEUl5nwM=
 github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce/go.mod h1:HXgVzOPvXhVGLJs4ZKO817idqr/xhwsTcj17CLYY74s=
-github.com/aquasecurity/go-npm-version v0.0.1 h1:2i/MM+A4KI8AJrqJa/Cwsa4qyljA8S/qngPyQiIVHcA=
-github.com/aquasecurity/go-npm-version v0.0.1/go.mod h1:hxbJZtKlO4P8sZ9nztizR6XLoE33O+BkPmuYQ4ACyz0=
+github.com/aquasecurity/go-npm-version v0.0.2 h1:6sNIaeW4Hw8Xg51nPoD3VSo/5qmFSu0VL809iehEOvc=
+github.com/aquasecurity/go-npm-version v0.0.2/go.mod h1:DXyKqRe2yb83peANMjQr8dGDkHanEgoFv8BOQdWlSUQ=
 github.com/aquasecurity/go-pep440-version v0.0.1 h1:8VKKQtH2aV61+0hovZS3T//rUF+6GDn18paFTVS0h0M=
 github.com/aquasecurity/go-pep440-version v0.0.1/go.mod h1:3naPe+Bp6wi3n4l5iBFCZgS0JG8vY6FT0H4NGhFJ+i4=
 github.com/aquasecurity/go-version v0.0.0-20201107203531-5e48ac5d022a/go.mod h1:9Beu8XsUNNfzml7WBf3QmyPToP1wm1Gj/Vc5UJKqTzU=
@@ -829,8 +829,8 @@ github.com/aquasecurity/tml v0.6.1 h1:y2ZlGSfrhnn7t4ZJ/0rotuH+v5Jgv6BDDO5jB6A9gw
 github.com/aquasecurity/tml v0.6.1/go.mod h1:OnYMWY5lvI9ejU7yH9LCberWaaTBW7hBFsITiIMY2yY=
 github.com/aquasecurity/trivy-checks v1.11.3-0.20250604022615-9a7efa7c9169 h1:TckzIxUX7lZaU9f2lNxCN0noYYP8fzmSQf6a4JdV83w=
 github.com/aquasecurity/trivy-checks v1.11.3-0.20250604022615-9a7efa7c9169/go.mod h1:nT69xgRcBD4NlHwTBpWMYirpK5/Zpl8M+XDOgmjMn2k=
-github.com/aquasecurity/trivy-db v0.0.0-20250627124416-ca81c496a932 h1:5GKQ53uGGHEEtZ/FX94jcIdfEGeyiHZ7tmJ5nCtDz5c=
-github.com/aquasecurity/trivy-db v0.0.0-20250627124416-ca81c496a932/go.mod h1:Ubl2YWA6Zg7eaojg4MDmeDdYU4+PiGPsnwo6B5UIwqw=
+github.com/aquasecurity/trivy-db v0.0.0-20250716122853-45f09ec4df9c h1:nWJKidnaCx50H0JvqzCQNr0Ew9sUO0QcnGSeDJajmvc=
+github.com/aquasecurity/trivy-db v0.0.0-20250716122853-45f09ec4df9c/go.mod h1:upAJqDQkN5FdIJbtJMpokncGNhYAPGkpoCbaGciWPt4=
 github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48 h1:JVgBIuIYbwG+ekC5lUHUpGJboPYiCcxiz06RCtz8neI=
 github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48/go.mod h1:Ldya37FLi0e/5Cjq2T5Bty7cFkzUDwTcPeQua+2M8i8=
 github.com/aquasecurity/trivy-kubernetes v0.9.0 h1:rp8RuXwKfFWUPR/ULksA2WpD0z6rslVkzLmPGQr61Wc=
diff --git a/pkg/commands/artifact/run.go b/pkg/commands/artifact/run.go
index 0d24e9a06..5998e491d 100644
--- a/pkg/commands/artifact/run.go
+++ b/pkg/commands/artifact/run.go
@@ -126,7 +126,7 @@ func NewRunner(ctx context.Context, cliOptions flag.Options, opts ...RunnerOptio
 		TraceHTTP: cliOptions.TraceHTTP,
 	}))
 	// get the sub command that is being used or fallback to "trivy"
-	commandName := lo.Ternary(len(os.Args) > 1, os.Args[1], "trivy")
+	commandName := lo.NthOr(os.Args, 1, "trivy")
 	r.versionChecker = notification.NewVersionChecker(commandName, &cliOptions)
 
 	// Update the vulnerability database if needed.
diff --git a/pkg/detector/library/compare/npm/compare.go b/pkg/detector/library/compare/npm/compare.go
index 13e14b3c1..f3d5712e2 100644
--- a/pkg/detector/library/compare/npm/compare.go
+++ b/pkg/detector/library/compare/npm/compare.go
@@ -23,7 +23,7 @@ func (n Comparer) MatchVersion(currentVersion, constraint string) (bool, error)
 		return false, xerrors.Errorf("npm version error (%s): %s", currentVersion, err)
 	}
 
-	c, err := npm.NewConstraints(constraint)
+	c, err := npm.NewConstraints(constraint, npm.WithPreRelease(true))
 	if err != nil {
 		return false, xerrors.Errorf("npm constraint error (%s): %s", constraint, err)
 	}
diff --git a/pkg/detector/ospkg/alma/alma.go b/pkg/detector/ospkg/alma/alma.go
index a88257cb2..8c6a79747 100644
--- a/pkg/detector/ospkg/alma/alma.go
+++ b/pkg/detector/ospkg/alma/alma.go
@@ -20,8 +20,9 @@ var (
 	eolDates = map[string]time.Time{
 		// Source:
 		// https://endoflife.date/almalinux
-		"8": time.Date(2029, 3, 1, 23, 59, 59, 0, time.UTC),
-		"9": time.Date(2032, 5, 31, 23, 59, 59, 0, time.UTC),
+		"8":  time.Date(2029, 3, 1, 23, 59, 59, 0, time.UTC),
+		"9":  time.Date(2032, 5, 31, 23, 59, 59, 0, time.UTC),
+		"10": time.Date(2035, 5, 31, 23, 59, 59, 0, time.UTC),
 	}
 )
 
diff --git a/pkg/detector/ospkg/rootio/rootio.go b/pkg/detector/ospkg/rootio/rootio.go
index 9a8fa1c30..451241435 100644
--- a/pkg/detector/ospkg/rootio/rootio.go
+++ b/pkg/detector/ospkg/rootio/rootio.go
@@ -1,6 +1,7 @@
 package rootio
 
 import (
+	"cmp"
 	"context"
 	"strings"
 
@@ -81,7 +82,7 @@ func (s *Scanner) Detect(ctx context.Context, osVer string, _ *ftypes.Repository
 			if !s.isVulnerable(ctx, utils.FormatSrcVersion(pkg), adv) {
 				continue
 			}
-			vulns = append(vulns, types.DetectedVulnerability{
+			vuln := types.DetectedVulnerability{
 				VulnerabilityID:  adv.VulnerabilityID,
 				PkgID:            pkg.ID,
 				PkgName:          pkg.Name,
@@ -91,7 +92,20 @@ func (s *Scanner) Detect(ctx context.Context, osVer string, _ *ftypes.Repository
 				PkgIdentifier:    pkg.Identifier,
 				Custom:           adv.Custom,
 				DataSource:       adv.DataSource,
-			})
+			}
+
+			// We add the severity from the base OS, so we need to keep the severity level from the base OS (as SeveritySource).
+			if adv.Severity != dbTypes.SeverityUnknown {
+				vuln.Vulnerability = dbTypes.Vulnerability{
+					Severity: adv.Severity.String(),
+				}
+
+				// Datasource contains BaseID + ID for root.io advisories,
+				// But baseOS (e.g. Debian) advisories use ID only.
+				vuln.SeveritySource = cmp.Or(adv.DataSource.BaseID, adv.DataSource.ID)
+			}
+
+			vulns = append(vulns, vuln)
 		}
 	}
 	return vulns, nil
diff --git a/pkg/iac/adapters/arm/storage/adapt.go b/pkg/iac/adapters/arm/storage/adapt.go
index 871512cd2..cbc0b1553 100644
--- a/pkg/iac/adapters/arm/storage/adapt.go
+++ b/pkg/iac/adapters/arm/storage/adapt.go
@@ -6,6 +6,7 @@ import (
 	"github.com/aquasecurity/trivy/pkg/iac/providers/azure/storage"
 	"github.com/aquasecurity/trivy/pkg/iac/scanners/azure"
 	"github.com/aquasecurity/trivy/pkg/iac/types"
+	xslices "github.com/aquasecurity/trivy/pkg/x/slices"
 )
 
 func Adapt(deployment azure.Deployment) storage.Storage {
@@ -18,20 +19,6 @@ func adaptAccounts(deployment azure.Deployment) []storage.Account {
 	var accounts []storage.Account
 	for _, resource := range deployment.GetResourcesByType("Microsoft.Storage/storageAccounts") {
 
-		acl := resource.Properties.GetMapValue("networkAcls")
-
-		var bypasses []types.StringValue
-		bypassProp := acl.GetMapValue("bypass")
-		for bypass := range strings.SplitSeq(bypassProp.AsString(), ",") {
-			bypasses = append(bypasses, types.String(strings.TrimSpace(bypass), bypassProp.GetMetadata()))
-		}
-
-		networkRule := storage.NetworkRule{
-			Metadata:       acl.GetMetadata(),
-			Bypass:         bypasses,
-			AllowByDefault: types.Bool(acl.GetMapValue("defaultAction").EqualTo("Allow"), acl.GetMetadata()),
-		}
-
 		var queues []storage.Queue
 		for _, queueResource := range resource.GetResourcesByType("queueServices/queues") {
 			queues = append(queues, storage.Queue{
@@ -50,15 +37,18 @@ func adaptAccounts(deployment azure.Deployment) []storage.Account {
 
 		account := storage.Account{
 			Metadata:     resource.Metadata,
-			NetworkRules: []storage.NetworkRule{networkRule},
-			EnforceHTTPS: resource.Properties.GetMapValue("supportsHttpsTrafficOnly").AsBoolValue(false, resource.Properties.GetMetadata()),
+			NetworkRules: xslices.ZeroToNil(adaptNetworkRules(resource)),
+			// The default value is true since API version 2019-04-01.
+			EnforceHTTPS: resource.Properties.GetMapValue("supportsHttpsTrafficOnly").AsBoolValue(true, resource.Properties.GetMetadata()),
 			Containers:   containers,
 			QueueProperties: storage.QueueProperties{
 				Metadata:      resource.Properties.GetMetadata(),
 				EnableLogging: types.BoolDefault(false, resource.Properties.GetMetadata()),
 			},
-			MinimumTLSVersion: resource.Properties.GetMapValue("minimumTlsVersion").AsStringValue("", resource.Properties.GetMetadata()),
-			Queues:            queues,
+			// The default interpretation is TLS 1.0 for this property.
+			MinimumTLSVersion: resource.Properties.GetMapValue("minimumTlsVersion").
+				AsStringValue("TLS1_0", resource.Properties.GetMetadata()),
+			Queues: queues,
 		}
 
 		publicNetworkAccess := resource.Properties.GetMapValue("publicNetworkAccess")
@@ -70,3 +60,41 @@ func adaptAccounts(deployment azure.Deployment) []storage.Account {
 	}
 	return accounts
 }
+
+func adaptNetworkRules(resource azure.Resource) []storage.NetworkRule {
+	defaultBypasses := []types.StringValue{types.StringDefault("AzureServices", resource.Metadata)}
+	acl := resource.Properties.GetMapValue("networkAcls")
+	if acl.IsNull() {
+		// default network rule
+		return []storage.NetworkRule{{
+			Metadata:       resource.Metadata,
+			Bypass:         defaultBypasses,
+			AllowByDefault: types.BoolDefault(true, resource.Metadata),
+		}}
+	}
+
+	bypassProp := acl.GetMapValue("bypass")
+	bypassVal := bypassProp.AsString()
+
+	var bypasses []types.StringValue
+	if bypassVal != "" {
+		// Possible values are any combination of Logging|Metrics|AzureServices (For example, "Logging, Metrics")
+		// See https://github.com/Azure/azure-resource-manager-schemas/blob/0cb6180c9646c91ca212de0e59568c00ee3a47ec/schemas/2021-01-01/Microsoft.Storage.json#L2379
+		for bypass := range strings.SplitSeq(bypassVal, ",") {
+			bypasses = append(bypasses, types.String(strings.TrimSpace(bypass), bypassProp.GetMetadata()))
+		}
+	} else {
+		bypasses = defaultBypasses
+	}
+
+	allowByDefault := types.Bool(true, acl.GetMetadata())
+	if defaultAction := acl.GetMapValue("defaultAction"); !defaultAction.IsNull() {
+		allowByDefault = types.Bool(defaultAction.EqualTo("Allow"), defaultAction.GetMetadata())
+	}
+
+	return []storage.NetworkRule{{
+		Metadata:       acl.GetMetadata(),
+		Bypass:         bypasses,
+		AllowByDefault: allowByDefault,
+	}}
+}
diff --git a/pkg/iac/scanners/azure/value.go b/pkg/iac/scanners/azure/value.go
index 381a663ac..1e216de9d 100644
--- a/pkg/iac/scanners/azure/value.go
+++ b/pkg/iac/scanners/azure/value.go
@@ -288,7 +288,11 @@ func (v Value) GetMapValue(key string) Value {
 	if v.Kind != KindObject {
 		return NullValue
 	}
-	return v.rMap[key]
+	v, exists := v.rMap[key]
+	if !exists {
+		return NullValue
+	}
+	return v
 }
 
 func (v Value) AsMap() map[string]Value {
@@ -370,3 +374,7 @@ func (v Value) AsStringValuesList(defaultValue string) (stringValues []types.Str
 
 	return stringValues
 }
+
+func (v Value) IsNull() bool {
+	return v.Kind == KindNull
+}
diff --git a/pkg/sbom/io/decode.go b/pkg/sbom/io/decode.go
index 0fe9c06d4..f45da396f 100644
--- a/pkg/sbom/io/decode.go
+++ b/pkg/sbom/io/decode.go
@@ -391,8 +391,14 @@ func (m *Decoder) addOrphanPkgs(sbom *types.SBOM) error {
 	for _, pkgs := range osPkgMap {
 		// TODO: mismatch between the OS and the packages should be rejected.
 		// e.g. OS: debian, Packages: rpm
-		sort.Sort(pkgs)
-		sbom.Packages = append(sbom.Packages, ftypes.PackageInfo{Packages: pkgs})
+
+		// addOSPkgs creates exactly one PackageInfo with empty FilePath, so we can merge directly
+		if len(sbom.Packages) == 0 {
+			sbom.Packages = append(sbom.Packages, ftypes.PackageInfo{})
+		}
+		// Merge with existing PackageInfo (always sbom.Packages[0])
+		sbom.Packages[0].Packages = append(sbom.Packages[0].Packages, pkgs...)
+		sort.Sort(sbom.Packages[0].Packages)
 
 		break // Just take the first element
 	}
diff --git a/pkg/vulnerability/vulnerability.go b/pkg/vulnerability/vulnerability.go
index 971da8ca0..a1a1a9474 100644
--- a/pkg/vulnerability/vulnerability.go
+++ b/pkg/vulnerability/vulnerability.go
@@ -1,6 +1,7 @@
 package vulnerability
 
 import (
+	"cmp"
 	"slices"
 	"strings"
 	"sync"
@@ -91,8 +92,12 @@ func (c Client) FillInfo(vulns []types.DetectedVulnerability, severitySources []
 		// Detect the data source
 		dataSource := lo.FromPtr(vulns[i].DataSource)
 
+		// To determine severity, use BaseID if available, otherwise use ID.
+		// e.g. `debian` severity for root.io advisories.
+		dataSourceID := cmp.Or(dataSource.BaseID, dataSource.ID)
+
 		// Select the severity according to the detected sourceID.
-		severity, severitySource := c.getSeverity(vulnID, &vuln, dataSource, severitySources)
+		severity, severitySource := c.getSeverity(vulnID, &vuln, dataSourceID, severitySources)
 		if severity == dbTypes.SeverityUnknown.String() {
 			noSeverityIDs = append(noSeverityIDs, vulnID)
 		}
@@ -126,10 +131,10 @@ func (c Client) FillInfo(vulns []types.DetectedVulnerability, severitySources []
 	}
 }
 
-func (c Client) getSeverity(vulnID string, vuln *dbTypes.Vulnerability, dataSource dbTypes.DataSource, severitySources []dbTypes.SourceID) (string, dbTypes.SourceID) {
+func (c Client) getSeverity(vulnID string, vuln *dbTypes.Vulnerability, dataSourceID dbTypes.SourceID, severitySources []dbTypes.SourceID) (string, dbTypes.SourceID) {
 	for _, source := range severitySources {
 		if source == "auto" {
-			return c.autoDetectSeverity(vulnID, vuln, dataSource)
+			return c.autoDetectSeverity(vulnID, vuln, dataSourceID)
 		}
 
 		if severity, ok := vuln.VendorSeverity[source]; ok {
@@ -149,19 +154,19 @@ func (c Client) getSeverity(vulnID string, vuln *dbTypes.Vulnerability, dataSour
 //  3. Use the severity from NVD as a fallback.
 //  4. Try severities from other data sources (e.g., Debian severity for Red Hat distributions).
 //  5. If no severity is found from any data source, return "UNKNOWN".
-func (c Client) autoDetectSeverity(vulnID string, vuln *dbTypes.Vulnerability, dataSource dbTypes.DataSource) (string, dbTypes.SourceID) {
-	autoSeveritySrcs := []dbTypes.SourceID{dataSource.ID, vulnerability.NVD}
-	if vs, ok := vuln.VendorSeverity[dataSource.ID]; ok {
-		return vs.String(), dataSource.ID
+func (c Client) autoDetectSeverity(vulnID string, vuln *dbTypes.Vulnerability, dataSourceID dbTypes.SourceID) (string, dbTypes.SourceID) {
+	autoSeveritySrcs := []dbTypes.SourceID{dataSourceID, vulnerability.NVD}
+	if vs, ok := vuln.VendorSeverity[dataSourceID]; ok {
+		return vs.String(), dataSourceID
 	}
 
 	// use severity from GitHub for all GHSA-xxx vulnerabilities
 	if strings.HasPrefix(vulnID, "GHSA-") {
 		// use severity from GitHub for all GHSA-IDs
-		autoSeveritySrcs = []dbTypes.SourceID{dataSource.ID, vulnerability.GHSA, vulnerability.NVD}
+		autoSeveritySrcs = []dbTypes.SourceID{dataSourceID, vulnerability.GHSA, vulnerability.NVD}
 	}
 
-	if severity, severitySource := c.getSeverity(vulnID, vuln, dataSource, autoSeveritySrcs); severity != dbTypes.SeverityUnknown.String() {
+	if severity, severitySource := c.getSeverity(vulnID, vuln, dataSourceID, autoSeveritySrcs); severity != dbTypes.SeverityUnknown.String() {
 		return severity, severitySource
 	}
 


Test patch:
diff --git a/pkg/detector/library/compare/npm/compare_test.go b/pkg/detector/library/compare/npm/compare_test.go
index 1d232e051..fab726e42 100644
--- a/pkg/detector/library/compare/npm/compare_test.go
+++ b/pkg/detector/library/compare/npm/compare_test.go
@@ -30,6 +30,17 @@ func TestNpmComparer_IsVulnerable(t *testing.T) {
 			},
 			want: true,
 		},
+		{
+			name: "prerelease",
+			args: args{
+				currentVersion: "1.45.1-lts.1",
+				advisory: dbTypes.Advisory{
+					VulnerableVersions: []string{">=1.4.4-lts.1, <2.0.0"},
+					PatchedVersions:    []string{"2.0.0"},
+				},
+			},
+			want: true,
+		},
 		{
 			name: "no patch",
 			args: args{
@@ -68,8 +79,12 @@ func TestNpmComparer_IsVulnerable(t *testing.T) {
 			args: args{
 				currentVersion: "2.0.0",
 				advisory: dbTypes.Advisory{
-					VulnerableVersions: []string{">=1.7.0 <1.7.16", ">=1.8.0 <1.8.8", ">=2.0.0 <2.0.8", ">=3.0.0-beta.1 <3.0.0-beta.7"},
-					PatchedVersions:    []string{">=3.0.0-beta.7", ">=2.0.8 <3.0.0-beta.1", ">=1.8.8 <2.0.0", ">=1.7.16 <1.8.0"},
+					VulnerableVersions: []string{
+						">=1.7.0 <1.7.16", ">=1.8.0 <1.8.8", ">=2.0.0 <2.0.8", ">=3.0.0-beta.1 <3.0.0-beta.7",
+					},
+					PatchedVersions: []string{
+						">=3.0.0-beta.7", ">=2.0.8 <3.0.0-beta.1", ">=1.8.8 <2.0.0", ">=1.7.16 <1.8.0",
+					},
 				},
 			},
 			want: true,
diff --git a/pkg/detector/ospkg/rootio/rootio_test.go b/pkg/detector/ospkg/rootio/rootio_test.go
index cacaa2c7b..0348e9b4a 100644
--- a/pkg/detector/ospkg/rootio/rootio_test.go
+++ b/pkg/detector/ospkg/rootio/rootio_test.go
@@ -48,13 +48,33 @@ func TestScanner_Detect(t *testing.T) {
 			want: []types.DetectedVulnerability{
 				{
 					PkgName:          "openssl",
-					VulnerabilityID:  "CVE-2024-13176",
+					VulnerabilityID:  "CVE-2024-13176", // Debian and Root.io contain this CVE
 					InstalledVersion: "3.0.15-1~deb12u1.root.io.0",
 					FixedVersion:     "3.0.15-1~deb12u1.root.io.1, 3.0.16-1~deb12u1",
+					SeveritySource:   vulnerability.Debian,
 					DataSource: &dbTypes.DataSource{
-						ID:   vulnerability.RootIO,
-						Name: "Root.io Security Patches",
-						URL:  "https://api.root.io/external/patch_feed",
+						ID:     vulnerability.RootIO,
+						BaseID: vulnerability.Debian,
+						Name:   "Root.io Security Patches (debian)",
+						URL:    "https://api.root.io/external/patch_feed",
+					},
+					Vulnerability: dbTypes.Vulnerability{
+						Severity: dbTypes.SeverityMedium.String(),
+					},
+				},
+				{
+					PkgName:          "openssl",
+					VulnerabilityID:  "CVE-2025-27587", // Debian only contains this CVE
+					InstalledVersion: "3.0.15-1~deb12u1.root.io.0",
+					FixedVersion:     "3.0.16-1~deb12u1",
+					SeveritySource:   vulnerability.Debian,
+					DataSource: &dbTypes.DataSource{
+						ID:   vulnerability.Debian,
+						Name: "Debian Security Tracker",
+						URL:  "https://salsa.debian.org/security-tracker-team/security-tracker",
+					},
+					Vulnerability: dbTypes.Vulnerability{
+						Severity: dbTypes.SeverityLow.String(),
 					},
 				},
 			},
@@ -84,9 +104,10 @@ func TestScanner_Detect(t *testing.T) {
 					InstalledVersion: "1.22.1-9+deb12u2.root.io.0",
 					FixedVersion:     "1.22.1-9+deb12u2.root.io.1",
 					DataSource: &dbTypes.DataSource{
-						ID:   vulnerability.RootIO,
-						Name: "Root.io Security Patches",
-						URL:  "https://api.root.io/external/patch_feed",
+						ID:     vulnerability.RootIO,
+						BaseID: vulnerability.Ubuntu,
+						Name:   "Root.io Security Patches (ubuntu)",
+						URL:    "https://api.root.io/external/patch_feed",
 					},
 				},
 			},
@@ -116,9 +137,10 @@ func TestScanner_Detect(t *testing.T) {
 					InstalledVersion: "643-r00072",
 					FixedVersion:     "643-r10072",
 					DataSource: &dbTypes.DataSource{
-						ID:   vulnerability.RootIO,
-						Name: "Root.io Security Patches",
-						URL:  "https://api.root.io/external/patch_feed",
+						ID:     vulnerability.RootIO,
+						BaseID: vulnerability.Alpine,
+						Name:   "Root.io Security Patches (alpine)",
+						URL:    "https://api.root.io/external/patch_feed",
 					},
 				},
 			},
diff --git a/pkg/detector/ospkg/rootio/testdata/fixtures/data-source.yaml b/pkg/detector/ospkg/rootio/testdata/fixtures/data-source.yaml
index ff3e8a3c8..e863ef4d7 100644
--- a/pkg/detector/ospkg/rootio/testdata/fixtures/data-source.yaml
+++ b/pkg/detector/ospkg/rootio/testdata/fixtures/data-source.yaml
@@ -1,17 +1,25 @@
 - bucket: data-source
   pairs:
+    - key: debian 12
+      value:
+        ID: "debian"
+        Name: "Debian Security Tracker"
+        URL: "https://salsa.debian.org/security-tracker-team/security-tracker"
     - key: root.io debian 12
       value:
         ID: "rootio"
-        Name: "Root.io Security Patches"
+        BaseID: "debian"
+        Name: "Root.io Security Patches (debian)"
         URL: "https://api.root.io/external/patch_feed"
     - key: root.io ubuntu 20.04
       value:
         ID: "rootio"
-        Name: "Root.io Security Patches"
+        BaseID: "ubuntu"
+        Name: "Root.io Security Patches (ubuntu)"
         URL: "https://api.root.io/external/patch_feed"
     - key: root.io alpine 3.19
       value:
         ID: "rootio"
-        Name: "Root.io Security Patches"
+        BaseID: "alpine"
+        Name: "Root.io Security Patches (alpine)"
         URL: "https://api.root.io/external/patch_feed"
\ No newline at end of file
diff --git a/pkg/detector/ospkg/rootio/testdata/fixtures/rootio.yaml b/pkg/detector/ospkg/rootio/testdata/fixtures/rootio.yaml
index a1853365f..81ffc767e 100644
--- a/pkg/detector/ospkg/rootio/testdata/fixtures/rootio.yaml
+++ b/pkg/detector/ospkg/rootio/testdata/fixtures/rootio.yaml
@@ -1,3 +1,21 @@
+- bucket: debian 12
+  pairs:
+    - bucket: openssl
+      pairs:
+        - key: CVE-2024-13176
+          value:
+            VulnerableVersions:
+              - "<3.0.16-1~deb12u1"
+            PatchedVersions:
+              - "3.0.16-1~deb12u1"
+            Severity: 2
+        - key: CVE-2025-27587
+          value:
+            VulnerableVersions:
+              - "<3.0.16-1~deb12u1"
+            PatchedVersions:
+              - "3.0.16-1~deb12u1"
+            Severity: 1
 - bucket: root.io debian 12
   pairs:
     - bucket: openssl
diff --git a/pkg/iac/adapters/arm/storage/adapt_test.go b/pkg/iac/adapters/arm/storage/adapt_test.go
index 708594366..56739f97c 100644
--- a/pkg/iac/adapters/arm/storage/adapt_test.go
+++ b/pkg/iac/adapters/arm/storage/adapt_test.go
@@ -26,8 +26,11 @@ func TestAdapt(t *testing.T) {
 }`,
 			expected: storage.Storage{
 				Accounts: []storage.Account{{
+					MinimumTLSVersion: types.StringTest("TLS1_0"),
+					EnforceHTTPS:      types.BoolTest(true),
 					NetworkRules: []storage.NetworkRule{{
-						Bypass: []types.StringValue{types.StringTest("")},
+						Bypass:         []types.StringValue{types.StringTest("AzureServices")},
+						AllowByDefault: types.BoolTest(true),
 					}},
 					PublicNetworkAccess: types.BoolTest(true),
 				}},
diff --git a/pkg/sbom/io/decode_test.go b/pkg/sbom/io/decode_test.go
index 782e7a3e9..0669499ab 100644
--- a/pkg/sbom/io/decode_test.go
+++ b/pkg/sbom/io/decode_test.go
@@ -87,6 +87,24 @@ var (
 		},
 		Licenses: []string{"GPL-2.0"},
 	}
+
+	orphanedApkComponent = &core.Component{
+		Type:    core.TypeLibrary,
+		Name:    "orphaned-package",
+		Version: "1.0.0",
+		PkgIdentifier: ftypes.PkgIdentifier{
+			PURL: &packageurl.PackageURL{
+				Type:    packageurl.TypeApk,
+				Name:    "orphaned-package",
+				Version: "1.0.0",
+				Qualifiers: packageurl.Qualifiers{
+					{Key: "arch", Value: "aarch64"},
+					{Key: "distro", Value: "wolfi-20230201"},
+				},
+			},
+		},
+		Licenses: []string{"MIT"},
+	}
 )
 
 func TestDecoder_Decode_OSPackages(t *testing.T) {
@@ -141,16 +159,17 @@ func TestDecoder_Decode_OSPackages(t *testing.T) {
 			},
 		},
 		{
-			name: "multiple OS packages without OS metadata should be included",
+			name: "multiple OS packages without OS metadata should be included and merged",
 			setupBOM: func() *core.BOM {
 				bom := core.NewBOM(core.Options{})
 				bom.SerialNumber = "test-multiple-no-os"
 				bom.Version = 1
 
-				// Add multiple APK packages
+				// Add multiple APK packages including orphaned package
 				bom.AddComponent(apkToolsComponent)
 				bom.AddComponent(busyboxComponent)
 				bom.AddComponent(caCertificatesComponent)
+				bom.AddComponent(orphanedApkComponent)
 
 				return bom
 			},
@@ -197,6 +216,18 @@ func TestDecoder_Decode_OSPackages(t *testing.T) {
 									PURL: caCertificatesComponent.PkgIdentifier.PURL,
 								},
 							},
+							{
+								ID:         "orphaned-package@1.0.0",
+								Name:       "orphaned-package",
+								Version:    "1.0.0",
+								Arch:       "aarch64",
+								SrcName:    "orphaned-package",
+								SrcVersion: "1.0.0",
+								Licenses:   []string{"MIT"},
+								Identifier: ftypes.PkgIdentifier{
+									PURL: orphanedApkComponent.PkgIdentifier.PURL,
+								},
+							},
 						},
 					},
 				},
@@ -233,6 +264,66 @@ func TestDecoder_Decode_OSPackages(t *testing.T) {
 				},
 			},
 		},
+		{
+			name: "mixed OS packages (in-graph and out-of-graph) should be merged",
+			setupBOM: func() *core.BOM {
+				bom := core.NewBOM(core.Options{})
+				bom.SerialNumber = "test-mixed-os-packages"
+				bom.Version = 1
+
+				// Add OS component
+				bom.AddComponent(wolfiOSComponent)
+
+				// Add OS packages - busybox is connected to OS (in-graph)
+				bom.AddComponent(busyboxComponent)
+				// Add orphaned package not connected to OS (out-of-graph)
+				bom.AddComponent(orphanedApkComponent)
+
+				// Create relationship between OS and busybox only
+				bom.AddRelationship(wolfiOSComponent, busyboxComponent, core.RelationshipContains)
+				// orphanedApkComponent is not connected to OS component
+
+				return bom
+			},
+			wantSBOM: types.SBOM{
+				Metadata: types.Metadata{
+					OS: &ftypes.OS{
+						Family: ftypes.Wolfi,
+						Name:   "20230201",
+					},
+				},
+				Packages: []ftypes.PackageInfo{
+					{
+						Packages: ftypes.Packages{
+							{
+								ID:         "busybox@1.37.0-r42",
+								Name:       "busybox",
+								Version:    "1.37.0-r42",
+								Arch:       "aarch64",
+								SrcName:    "busybox",
+								SrcVersion: "1.37.0-r42",
+								Licenses:   []string{"GPL-2.0-only"},
+								Identifier: ftypes.PkgIdentifier{
+									PURL: busyboxComponent.PkgIdentifier.PURL,
+								},
+							},
+							{
+								ID:         "orphaned-package@1.0.0",
+								Name:       "orphaned-package",
+								Version:    "1.0.0",
+								Arch:       "aarch64",
+								SrcName:    "orphaned-package",
+								SrcVersion: "1.0.0",
+								Licenses:   []string{"MIT"},
+								Identifier: ftypes.PkgIdentifier{
+									PURL: orphanedApkComponent.PkgIdentifier.PURL,
+								},
+							},
+						},
+					},
+				},
+			},
+		},
 	}
 
 	for _, tt := range tests {
@@ -252,7 +343,7 @@ func TestDecoder_Decode_OSPackages(t *testing.T) {
 			tt.wantSBOM.BOM = gotSBOM.BOM
 
 			// Compare the entire SBOM structure
-			assert.Equal(t, tt.wantSBOM, gotSBOM)
+			assert.EqualExportedValues(t, tt.wantSBOM, gotSBOM)
 		})
 	}
 }
diff --git a/pkg/vulnerability/testdata/fixtures/vulnerability.yaml b/pkg/vulnerability/testdata/fixtures/vulnerability.yaml
index eb4acaa65..1e9f8b404 100644
--- a/pkg/vulnerability/testdata/fixtures/vulnerability.yaml
+++ b/pkg/vulnerability/testdata/fixtures/vulnerability.yaml
@@ -64,6 +64,16 @@
           ghsa: 3
         References:
           - http://example.com
+    - key: CVE-2023-0001
+      value:
+        Title: dos
+        Description: dos vulnerability
+        VendorSeverity:
+          nvd: 1
+          alpine: 2
+          ubuntu: 3
+        References:
+          - http://example.com
     - key: GHSA-0000-aaaa-1111
       value:
         Title: dos
@@ -72,4 +82,4 @@
           nvd: 1
           ghsa: 3
         References:
-          - http://example.com
+          - http://example.com
\ No newline at end of file
diff --git a/pkg/vulnerability/vulnerability_test.go b/pkg/vulnerability/vulnerability_test.go
index 1c8620062..858a38af4 100644
--- a/pkg/vulnerability/vulnerability_test.go
+++ b/pkg/vulnerability/vulnerability_test.go
@@ -371,6 +371,91 @@ func TestClient_FillInfo(t *testing.T) {
 				},
 			},
 		},
+		{
+			name:     "happy path. Use alpine severity for root.io vulnerability",
+			fixtures: []string{"testdata/fixtures/vulnerability.yaml"},
+			vulns: []types.DetectedVulnerability{
+				{
+					VulnerabilityID: "CVE-2023-0001",
+					DataSource: &dbTypes.DataSource{
+						ID:     vulnerability.RootIO,
+						BaseID: vulnerability.Alpine,
+						Name:   "Root.io Security Patches (alpine)",
+						URL:    "https://api.root.io/external/patch_feed",
+					},
+				},
+			},
+			expectedVulnerabilities: []types.DetectedVulnerability{
+				{
+					VulnerabilityID: "CVE-2023-0001",
+					Status:          dbTypes.StatusAffected,
+					SeveritySource:  vulnerability.Alpine,
+					DataSource: &dbTypes.DataSource{
+						ID:     vulnerability.RootIO,
+						BaseID: vulnerability.Alpine,
+						Name:   "Root.io Security Patches (alpine)",
+						URL:    "https://api.root.io/external/patch_feed",
+					},
+					Vulnerability: dbTypes.Vulnerability{
+						Title:       "dos",
+						Description: "dos vulnerability",
+						Severity:    dbTypes.SeverityMedium.String(),
+						References:  []string{"http://example.com"},
+						VendorSeverity: map[dbTypes.SourceID]dbTypes.Severity{
+							"nvd":    dbTypes.SeverityLow,
+							"alpine": dbTypes.SeverityMedium,
+							"ubuntu": dbTypes.SeverityHigh,
+						},
+					},
+					PrimaryURL: "https://avd.aquasec.com/nvd/cve-2023-0001",
+				},
+			},
+		},
+		{
+			name:     "happy path. Use debian severity for root.io vulnerability",
+			fixtures: []string{"testdata/fixtures/vulnerability.yaml"},
+			vulns: []types.DetectedVulnerability{
+				{
+					VulnerabilityID: "CVE-2023-0001",
+					SeveritySource:  vulnerability.Debian,
+					DataSource: &dbTypes.DataSource{
+						ID:     vulnerability.RootIO,
+						BaseID: vulnerability.Debian,
+						Name:   "Root.io Security Patches (debian)",
+						URL:    "https://api.root.io/external/patch_feed",
+					},
+					Vulnerability: dbTypes.Vulnerability{
+						Severity: dbTypes.SeverityCritical.String(),
+					},
+				},
+			},
+			expectedVulnerabilities: []types.DetectedVulnerability{
+				{
+					VulnerabilityID: "CVE-2023-0001",
+					Status:          dbTypes.StatusAffected,
+					SeveritySource:  vulnerability.Debian,
+					DataSource: &dbTypes.DataSource{
+						ID:     vulnerability.RootIO,
+						BaseID: vulnerability.Debian,
+						Name:   "Root.io Security Patches (debian)",
+						URL:    "https://api.root.io/external/patch_feed",
+					},
+					Vulnerability: dbTypes.Vulnerability{
+						Title:       "dos",
+						Description: "dos vulnerability",
+						Severity:    dbTypes.SeverityCritical.String(),
+						References:  []string{"http://example.com"},
+						VendorSeverity: map[dbTypes.SourceID]dbTypes.Severity{
+							"nvd":    dbTypes.SeverityLow,
+							"alpine": dbTypes.SeverityMedium,
+							"ubuntu": dbTypes.SeverityHigh,
+							"debian": dbTypes.SeverityCritical,
+						},
+					},
+					PrimaryURL: "https://avd.aquasec.com/nvd/cve-2023-0001",
+				},
+			},
+		},
 		{
 			name:     "GetVulnerability returns an error",
 			fixtures: []string{"testdata/fixtures/sad.yaml"},