Repository: google/osv-scanner

Issue:
OSV Scanner scanning Podfile.lock and throws 400 Bad Request Invalid ecosystem since 2.2.0
The previous version (2.1.0) did not detect Podfile.lock, but the new one does and throws an error.
This issue is reproducible in Flutter when scanning recursively.

Error:
```
Scanned /src/ios/Podfile.lock file and found 50 packages
Scanned /src/pubspec.lock file and found 256 packages
End status: 534 dirs visited, 2574 inodes visited, 2 Extract calls, 121.639539ms elapsed, 121.639839ms wall time
error when retrieving vulns: client error: status="400 Bad Request" body={"code":3,"message":"Invalid ecosystem."}
client error: status="400 Bad Request" body={"code":3,"message":"Invalid ecosystem."}
```

Hints from issue comments:
@another-rex: Thanks for the report, this should be fixed in the next patch release, which we'll try to release soon.

For now you should be able to workaround this issue with the new disable-plugins flag, e.g.:

```
osv-scanner scan --experimental-disable-plugins swift/podfilelock,swift/packageresolved /src/
```@another-rex: OSV-Scanner 2.2.1 fixes this issue :)

Solution patch:
diff --git a/cmd/osv-scanner/scan/source/__snapshots__/command_test.snap b/cmd/osv-scanner/scan/source/__snapshots__/command_test.snap
index a2d50fae..6603d656 100755
--- a/cmd/osv-scanner/scan/source/__snapshots__/command_test.snap
+++ b/cmd/osv-scanner/scan/source/__snapshots__/command_test.snap
@@ -3415,6 +3415,24 @@ Total 2 packages affected by 2 known vulnerabilities (1 Critical, 1 High, 0 Medi
 
 ---
 
+[TestCommand_MoreLockfiles/Package.resolved_-_Unsupported_ecosystem,_should_not_be_scanned - 1]
+
+---
+
+[TestCommand_MoreLockfiles/Package.resolved_-_Unsupported_ecosystem,_should_not_be_scanned - 2]
+could not determine extractor suitable to this file
+
+---
+
+[TestCommand_MoreLockfiles/Podfile.lock_-_Unsupported_ecosystem,_should_not_be_scanned - 1]
+
+---
+
+[TestCommand_MoreLockfiles/Podfile.lock_-_Unsupported_ecosystem,_should_not_be_scanned - 2]
+could not determine extractor suitable to this file
+
+---
+
 [TestCommand_MoreLockfiles/cabal.project.freeze - 1]
 Scanned <rootdir>/fixtures/locks-scalibr/cabal.project.freeze file and found 6 packages
 Total 1 package affected by 1 known vulnerability (0 Critical, 0 High, 0 Medium, 0 Low, 1 Unknown) from 1 ecosystem.
diff --git a/cmd/osv-scanner/scan/source/fixtures/locks-scalibr/Podfile.lock b/cmd/osv-scanner/scan/source/fixtures/locks-scalibr/Podfile.lock
new file mode 100644
index 00000000..428fd9e0
--- /dev/null
+++ b/cmd/osv-scanner/scan/source/fixtures/locks-scalibr/Podfile.lock
@@ -0,0 +1,42 @@
+PODS:
+  - GlossButtonNode (3.1.2):
+    - Texture/Core (~> 3)
+    - TextureSwiftSupport (>= 3.10.0)
+  - PINCache (3.0.3):
+    - PINCache/Arc-exception-safe (= 3.0.3)
+    - PINCache/Core (= 3.0.3)
+
+DEPENDENCIES:
+  - GlossButtonNode
+  - Reveal-SDK
+  - SwiftGen
+  - Texture
+  - TextureSwiftSupport
+  - TinyConstraints
+
+SPEC REPOS:
+  trunk:
+    - GlossButtonNode
+    - PINCache
+    - PINOperation
+    - PINRemoteImage
+    - Reveal-SDK
+    - SwiftGen
+    - Texture
+    - TextureSwiftSupport
+    - TinyConstraints
+
+SPEC CHECKSUMS:
+  GlossButtonNode: 4ea1197a744f2fb5fb875fe31caf17ded4762e8f
+  PINCache: 7a8fc1a691173d21dbddbf86cd515de6efa55086
+  PINOperation: 00c935935f1e8cf0d1e2d6b542e75b88fc3e5e20
+  PINRemoteImage: f1295b29f8c5e640e25335a1b2bd9d805171bd01
+  Reveal-SDK: effba1c940b8337195563c425a6b5862ec875caa
+  SwiftGen: a6d22010845f08fe18fbdf3a07a8e380fd22e0ea
+  Texture: 2e8ab2519452515f7f5a520f5a8f7e0a413abfa3
+  TextureSwiftSupport: c515c7927fab92d0d9485f49b885b8c5de34fbfb
+  TinyConstraints: 7b7ccc0c485bb3bb47082138ff28bc33cd49897f
+
+PODFILE CHECKSUM: 07aa55f54421f6e6d3a920c11716a89fc9243d1b
+
+COCOAPODS: 1.11.2
diff --git a/internal/imodels/ecosystem/ecosystem.go b/internal/imodels/ecosystem/ecosystem.go
index 8adb4ecc..c713a09b 100644
--- a/internal/imodels/ecosystem/ecosystem.go
+++ b/internal/imodels/ecosystem/ecosystem.go
@@ -91,6 +91,53 @@ func (p Parsed) String() string {
 	return str
 }
 
+func (p Parsed) IsValid() bool {
+	// Missing ecosystems here would be caught by the "exhaustive" linter
+	switch p.Ecosystem {
+	case osvschema.EcosystemAlmaLinux,
+		osvschema.EcosystemAlpaquita,
+		osvschema.EcosystemAlpine,
+		osvschema.EcosystemAndroid,
+		osvschema.EcosystemBellSoftHardenedContainers,
+		osvschema.EcosystemBioconductor,
+		osvschema.EcosystemBitnami,
+		osvschema.EcosystemChainguard,
+		osvschema.EcosystemConanCenter,
+		osvschema.EcosystemCRAN,
+		osvschema.EcosystemCratesIO,
+		osvschema.EcosystemDebian,
+		osvschema.EcosystemGHC,
+		osvschema.EcosystemGitHubActions,
+		osvschema.EcosystemGo,
+		osvschema.EcosystemHackage,
+		osvschema.EcosystemHex,
+		osvschema.EcosystemKubernetes,
+		osvschema.EcosystemLinux,
+		osvschema.EcosystemMageia,
+		osvschema.EcosystemMaven,
+		osvschema.EcosystemMinimOS,
+		osvschema.EcosystemNPM,
+		osvschema.EcosystemNuGet,
+		osvschema.EcosystemOpenEuler,
+		osvschema.EcosystemOpenSUSE,
+		osvschema.EcosystemOSSFuzz,
+		osvschema.EcosystemPackagist,
+		osvschema.EcosystemPhotonOS,
+		osvschema.EcosystemPub,
+		osvschema.EcosystemPyPI,
+		osvschema.EcosystemRedHat,
+		osvschema.EcosystemRockyLinux,
+		osvschema.EcosystemRubyGems,
+		osvschema.EcosystemSUSE,
+		osvschema.EcosystemSwiftURL,
+		osvschema.EcosystemUbuntu,
+		osvschema.EcosystemWolfi:
+		return true
+	}
+
+	return false
+}
+
 // MustParse parses a string into a constants.Ecosystem and an optional suffix specified with a ":"
 // Panics if there is an invalid ecosystem
 func MustParse(str string) Parsed {
diff --git a/internal/scalibrplugin/__snapshots__/resolve_test.snap b/internal/scalibrplugin/__snapshots__/resolve_test.snap
index 601ca2c2..3ae14a26 100755
--- a/internal/scalibrplugin/__snapshots__/resolve_test.snap
+++ b/internal/scalibrplugin/__snapshots__/resolve_test.snap
@@ -89,8 +89,6 @@ r/renvlock
 ruby/gemfilelock
 ruby/gemspec
 rust/cargolock
-swift/packageresolved
-swift/podfilelock
 ---
 
 [TestResolve_Extractors_Presets/sbom - 1]
diff --git a/internal/scalibrplugin/presets.go b/internal/scalibrplugin/presets.go
index 2af0be66..f58450ca 100644
--- a/internal/scalibrplugin/presets.go
+++ b/internal/scalibrplugin/presets.go
@@ -10,6 +10,8 @@ import (
 	"github.com/google/osv-scalibr/extractor/filesystem/language/python/requirements"
 	"github.com/google/osv-scalibr/extractor/filesystem/language/python/requirementsnet"
 	"github.com/google/osv-scalibr/extractor/filesystem/language/rust/cargotoml"
+	"github.com/google/osv-scalibr/extractor/filesystem/language/swift/packageresolved"
+	"github.com/google/osv-scalibr/extractor/filesystem/language/swift/podfilelock"
 	extractors "github.com/google/osv-scalibr/extractor/filesystem/list"
 	"github.com/google/osv-scalibr/extractor/filesystem/secrets"
 	"github.com/google/osv-scanner/v2/internal/scalibrextract/filesystem/vendored"
@@ -33,6 +35,9 @@ var ExtractorPresets = map[string]extractors.InitMap{
 			pomxml.Name, pomxmlnet.Name,
 			requirements.Name, requirementsnet.Name,
 			secrets.Name, cargotoml.Name,
+			// We don't support Cocoapods ecosystem
+			podfilelock.Name,
+			packageresolved.Name,
 		}),
 		extractors.InitMap{
 			pomxmlenhanceable.Name:      {pomxmlenhanceable.New},
diff --git a/pkg/osvscanner/filter.go b/pkg/osvscanner/filter.go
index 67ac86b0..7f8b7202 100644
--- a/pkg/osvscanner/filter.go
+++ b/pkg/osvscanner/filter.go
@@ -12,7 +12,8 @@ import (
 	"github.com/ossf/osv-schema/bindings/go/osvschema"
 )
 
-// filterUnscannablePackages removes packages that don't have enough information to be scanned
+// filterUnscannablePackages removes packages that don't have enough information to be scanned or
+// are not a supported ecosystem
 // e,g, local packages that specified by path
 func filterUnscannablePackages(scanResults *results.ScanResults) {
 	packageResults := make([]imodels.PackageScanResult, 0, len(scanResults.PackageScanResults))
@@ -20,14 +21,20 @@ func filterUnscannablePackages(scanResults *results.ScanResults) {
 		p := psr.PackageInfo
 
 		switch {
-		// If none of the cases match, skip this package since it's not scannable
+		// If **none** of the cases match, skip this package since it's not scannable
 		case !p.Ecosystem().IsEmpty() && p.Name() != "" && p.Version() != "":
 		case p.Commit() != "":
-		case p.Ecosystem().Ecosystem == osvschema.EcosystemMaven && p.Name() == "unknown":
 		default:
 			continue
 		}
 
+		switch {
+		// If **any** of the following cases are true, skip this package
+		case p.Ecosystem().Ecosystem == osvschema.EcosystemMaven && p.Name() == "unknown",
+			!p.Ecosystem().IsValid() && !p.Ecosystem().IsEmpty():
+			continue
+		}
+
 		packageResults = append(packageResults, psr)
 	}
 



Test patch:
diff --git a/cmd/osv-scanner/scan/source/command_test.go b/cmd/osv-scanner/scan/source/command_test.go
index 6d87a5a5..514935f2 100644
--- a/cmd/osv-scanner/scan/source/command_test.go
+++ b/cmd/osv-scanner/scan/source/command_test.go
@@ -960,13 +960,16 @@ func TestCommand_MoreLockfiles(t *testing.T) {
 			Args: []string{"", "source", "-L", "./fixtures/locks-scalibr/gems.locked"},
 			Exit: 1,
 		},
-		/*
-			{
-				name: "Package.resolved",
-				args: []string{"", "source", "-L", "./fixtures/locks-scalibr/Package.resolved"},
-				exit: 0,
-			},
-		*/
+		{
+			Name: "Podfile.lock - Unsupported ecosystem, should not be scanned",
+			Args: []string{"", "source", "-L", "./fixtures/locks-scalibr/Podfile.lock"},
+			Exit: 127,
+		},
+		{
+			Name: "Package.resolved - Unsupported ecosystem, should not be scanned",
+			Args: []string{"", "source", "-L", "./fixtures/locks-scalibr/Package.resolved"},
+			Exit: 127,
+		},
 	}
 
 	for _, tt := range tests {